Non ci sono restrizioni! Quando ho iniziato a studiare le basi teoriche di categoria per i costruttori di tipi, anche questo punto mi ha confuso. Ci arriveremo. Ma prima lasciami chiarire un po 'di confusione. Queste due citazioni:
tale funzione può avere solo come categoria target una categoria costruita usando un costruttore di tipi
e
si può pensare a funzioni aventi qualsiasi categoria come bersaglio di un funzione, ad esempio la categoria di tutti i tipi di Haskell
mostra che stai fraintendendo cosa sia un funzione (o per lo meno stai abusando della terminologia).
I portatori non costruiscono categorie. Un funzione è una mappatura tra le categorie. I portatori portano oggetti e morfismi (tipi e funzioni) nella categoria sorgente all'oggetto e morfismi nella categoria obiettivo.
Si noti che ciò significa che un funzione è in realtà una coppia di mappature: una mappatura su oggetti F_obj e una mappatura su morfismi F_morph . In Haskell, la parte oggetto F_obj del functor è il nome del tipo costruttore (ad es. List
), Mentre la parte morfismo è la funzione fmap
(spetta al compilatore Haskell per risolvere il problema a cui fmap
ci riferiamo in una determinata espressione). Pertanto, non possiamo dire che List
sia un funzione; solo la combinazione di List
ed fmap
è un funzione. Tuttavia, la gente abusa della notazione; i programmatori chiamano List
un funzione, mentre i teorici di categoria usano lo stesso simbolo per riferirsi ad entrambe le parti del funzione.
Inoltre, nella programmazione, quasi tutti i funzioni sono endofunctor , ovvero la categoria sorgente e destinazione sono le stesse - la categoria di tutti i tipi nella nostra lingua. Chiamiamo questa categoria Tipo . Un endofunctor F su Type mappa un tipo T su un altro tipo FT e una funzione T -> S su un'altra funzione FT -> FS . Questa mappatura deve ovviamente obbedire alle leggi dei funzioni.
Usando List
come esempio: abbiamo un costruttore di tipi List : Type -> Type
e una funzione fmap: (a -> b) -> (List a -> List b)
che insieme formano un funzione. T
C'è un ultimo punto da chiarire. La scrittura List int
non crea un nuovo tipo di elenchi di numeri interi. Questo tipo esisteva già . Era un oggetto nella nostra categoria Tipo . List Int
è semplicemente un modo per fare riferimento ad esso.
Ora ti stai chiedendo perché un functor non può mappare un tipo su, diciamo, Int
o String
. Ma può! Basta usare il identificatore. Per ogni categoria C , il funzione identità identifica ogni oggetto su se stesso e il morfismo su se stesso. È semplice verificare che questa mappatura soddisfi le leggi dei funzioni. In Haskell, questo sarebbe un costruttore di tipi id : * -> *
che mappa ogni tipo su se stesso. Ad esempio, id int
valuta int
.
Inoltre, si possono persino creare costanti funzioni, che mappano tutti i tipi su un singolo tipo. Ad esempio, il functor ToInt : * -> *
, dove ToInt a = int
per tutti i tipi a
, e associa tutti i morfismi alla funzione di identità intera: fmap f = \x -> x