W Haskell funktor typu Functor jest zdefiniowany następująco (patrz np. Wiki Haskell ):
class Functor (f :: * -> *) where
fmap :: (a -> b) -> f a -> f b
O ile mi zrozumieć (proszę mnie poprawić, jeśli się mylę), taki funktor może mieć tylko jako kategoria docelowej kategorii zbudowanego z użyciem konstruktora typu, na przykład []
, Maybe
itd Z drugiej strony, można myśleć o żadnej kategorii funktorów jako cel funktora, np. kategoria wszystkich typów Haskell. Na przykład Int
może być obiektem w docelowej kategorii funktora, nie tylko Maybe Int
lub [Int]
.
Jaka jest motywacja tego ograniczenia dla funktorów Haskell?
f
prawo? W twoim scenariuszuf
powinno być tak jak normalna funkcja Haskell i mapować typy na typy. W Haskell jedyne, co mogą mieć tego rodzaju,* -> *
to konstruktory typów. Rodziny typów są bardziej ogólne, ale zawsze muszą być w pełni stosowaneOdpowiedzi:
Nie ma żadnych ograniczeń! Kiedy zacząłem uczyć się teoretycznych podstaw dla konstruktorów typów, ten punkt również mnie zdezorientował. Dojdziemy do tego. Ale najpierw pozwól mi wyjaśnić pewne zamieszanie. Te dwa cytaty:
i
pokaż, że nie rozumiesz, czym jest funktor (a przynajmniej niewłaściwie używasz terminologii).
Functors nie konstruują kategorii. Funktor to odwzorowanie między kategoriami. Funktory wprowadzają obiekty i morfizmy (typy i funkcje) w kategorii źródłowej do obiektów i morfizmów w kategorii docelowej.
Zauważ, że oznacza to, że funktor to tak naprawdę para mapowań: mapowanie na obiektach F_obj i mapowanie na morfizmach F_morph . W Haskell, obiektowa część F_obj funktora jest nazwą konstruktora typu (np.
List
), Podczas gdy część morfizmu jest funkcjąfmap
(to do kompilatora Haskell należy uporządkowanie, o którymfmap
mowa w dowolnym wyrażeniu). Nie możemy zatem powiedzieć, żeList
jest to funktor; tylko połączenieList
ifmap
jest funktorem. Mimo to ludzie znoszą notację; programiści nazywająList
funktor, a teoretycy kategorii używają tego samego symbolu w odniesieniu do obu części funktora.Ponadto w programowaniu prawie wszystkie funktory są endofunkorami , to znaczy kategoria źródłowa i docelowa są takie same - kategoria wszystkich typów w naszym języku. Nazwijmy tę kategorię Typem . Endofunkcja F na Type mapuje typ T na inny typ FT, a funkcja T -> S na inną funkcję FT -> FS . To odwzorowanie musi oczywiście być zgodne z prawami funktorów.
Korzystając
List
z przykładu: mamy konstruktor typówList : Type -> Type
i funkcjęfmap: (a -> b) -> (List a -> List b)
, które razem tworzą funktor. T.Jest jeszcze jeden punkt do wyjaśnienia. Pisanie
List int
nie tworzy nowego typu list liczb całkowitych. Ten typ już istniał . To był obiekt w naszej kategorii Typ .List Int
jest po prostu sposobem na odniesienie się do tego.Zastanawiasz się teraz, dlaczego funktor nie może odwzorować typu na, powiedzmy,
Int
lubString
. Ale może! Wystarczy użyć funktora tożsamości. Dla każdej kategorii C funktor tożsamości odwzorowuje każdy obiekt na siebie, a morfizm na siebie. Łatwo jest sprawdzić, czy to odwzorowanie spełnia prawa funktora. W Haskell byłby to konstruktor typów,id : * -> *
który mapuje każdy typ na siebie. Na przykładid int
ocenia naint
.Co więcej, można nawet tworzyć stałe funktory, które mapują wszystkie typy na jeden typ. Na przykład funktor
ToInt : * -> *
, gdzieToInt a = int
dla wszystkich typówa
i mapuje wszystkie morfizmy na funkcję tożsamości liczb całkowitych:fmap f = \x -> x
źródło
f a
, gdzief
, o ile mi wiadomo, jest konstruktorem typów. Z tego, co pamiętam z teorii kategorii, musi to być pewnego rodzaju reprezentacja kanoniczna (obiekt początkowy w kategorii kategorii? Może niewłaściwie używam terminologii). W każdym razie dokładnie przeczytam twoją odpowiedź. Wielkie dzięki.[]
i:
. Miałem na myśli kanoniczną reprezentację.(_, int)
, który zajmuje rodzajua
do rodzaju produktu(a, int)
oraz funkcjęf : 'a -> 'b
dog : 'a * int -> 'a * int
nie jest indukcyjne.f : 'a -> 'b
dog : 'a * int -> 'b * int
?