Perché i funzioni di Haskell hanno solo tipi derivati ​​nella loro categoria target?


12

In Haskell, la funzione typeclass Functor è definita come segue (vedi ad esempio wiki Haskell ):

class Functor (f :: * -> *) where
  fmap :: (a -> b) -> f a -> f b 

Per quanto ho capito (per favore correggetemi se sbaglio), una tale funtore può avere solo come categoria bersaglio una categoria costruito utilizzando un costruttore di tipo, ad esempio [], Maybe, ecc D'altra parte, si può pensare a funtori aventi una categoria come target di un funzione, ad es. la categoria di tutti i tipi di Haskell. Ad esempio, Intpotrebbe essere un oggetto nella categoria target di un funzione, non solo Maybe Into [Int].

Qual è la motivazione di questa limitazione per i funzioni di Haskell?


4
Semplicità? Haskell non ha funzioni di tipo di prima classe, quindi tutte le funzioni sono in realtà solo costruttori di tipi.
Daniel Gratzer,

2
@jozefg: Perdonate la mia ignoranza: quali sono le "funzioni di tipo di prima classe"?
Giorgio,

4
Quindi in quella funzione stiamo girando attorno a un fdiritto? E nel tuo scenario, fdovrebbe essere proprio come una normale funzione di Haskell e mappare i tipi ai tipi. In Haskell, le uniche cose che possono avere il tipo * -> *sono costruttori di tipi. Le famiglie di tipi sono più generiche, ma devono sempre essere pienamente applicate
Daniel Gratzer,


@jozefg: ogni tanto penso a questa domanda ancora e ancora. Suppongo che la restrizione di Haskell non influisca sul potere espressivo dei funzioni. Ad esempio, supponiamo di avere un functor che è isomorfo al list list, ma che non mappi, diciamo, Int -> [Int] ma Int -> <tipo di fantasia che non usa nessun costruttore di tipo>. Quindi immagino che si possa dimostrare che <tipo di fantasia che non usa nessun costruttore di tipi> è isomorfo su [Int]. Quindi la scelta di oggetti definiti usando un costruttore di tipi è semplicemente conveniente e non sacrifica il potere espressivo.
Giorgio,

Risposte:


1

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 fmapci riferiamo in una determinata espressione). Pertanto, non possiamo dire che Listsia un funzione; solo la combinazione di Listed fmapè un funzione. Tuttavia, la gente abusa della notazione; i programmatori chiamano Listun 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 Listcome esempio: abbiamo un costruttore di tipi List : Type -> Typee una funzione fmap: (a -> b) -> (List a -> List b)che insieme formano un funzione. T

C'è un ultimo punto da chiarire. La scrittura List intnon 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, Into 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 intvaluta 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 = intper tutti i tipi a, e associa tutti i morfismi alla funzione di identità intera: fmap f = \x -> x


Grazie per la risposta, questa domanda ha più di due anni. "I portatori non costruiscono categorie": non l'ho detto. Ho detto che i funzione mappano due categorie, in cui la categoria target deve avere la forma f a, dove fper quanto ne so è un costruttore di tipi. Da quello che ricordo dalla teoria delle categorie, questa deve essere una sorta di rappresentazione canonica (oggetto iniziale in una categoria di categorie? Forse sto abusando della terminologia.) In ogni caso, leggerò attentamente la tua risposta. Grazie molto.
Giorgio,

@Giorgio urla, non ho notato quanti anni avesse ahah. È appena apparso in "domande senza risposta". Non sono sicuro di cosa intendi per "rappresentazione canonica". Per quanto ne so (e qui potrei sbagliarmi), non esiste una relazione tra i funzioni e gli oggetti iniziale / terminale.
gardenhead

Intendo questo: en.wikipedia.org/wiki/Initial_algebra (vedi Uso in informatica). In Haskell (la maggior parte) le funzioni sono definite su tipi di dati algebrici. L'oggetto target di tale funzione è un'algebra iniziale. L'algebra iniziale è isomorfa all'insieme di termini costruiti usando i costruttori di valori. Ad esempio, per elenchi []e :. Intendevo questo per rappresentazione canonica.
Giorgio,

Sì, so cos'è un oggetto iniziale e che i tipi di dati induttivi sono oggetti iniziali nell'algebra F di una categoria. Hai ragione sul fatto che molti costruttori di tipi sono definiti induttivamente. Ma questo non è strettamente necessario. Ad esempio, il funtore (_, int)che accetta un tipo aal tipo di prodotto (a, int)e una funzione f : 'a -> 'bper g : 'a * int -> 'a * intnon è di tipo induttivo.
gardenhead

Volevi dire: "prende ... una funzione f : 'a -> 'bper g : 'a * int -> 'b * int?
Giorgio,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.