2015-01-04 14 views
16

Quando studiavo i funtori in Haskell, ho trovato il tipo di funtore Functor.Indexed. Questo functor definisce un'operazione chiamata imap. Non ho capito la sua definizione e la firma imap: imap :: (a -> b) -> f j k a -> f j k b. Ho provato a trovare la sua definizione formale e ho trovato solo questo: http://ncatlab.org/nlab/show/indexed+functor. Ma davvero non mi ha aiutato affatto. Quindi qualcuno può chiarire in parole più semplici questo tipo di funtore e in quali casi dovrei usarlo? Grazie.Che cos'è esattamente un functor indicizzato in Haskell e quali sono i suoi usi?

+5

mia sensazione è che la motivazione dietro funtori indicizzati può essere compreso solo, cercando in funtori _applicative_ indicizzati, o monadi indicizzati. Queste sono proprio come le loro parti non indicizzate, tranne che il tipo (nel suo indice) può cambiare quando si usa '<*>' o '>> ='. L'indice può quindi essere utilizzato per es. per tenere traccia di quanti effetti collaterali hai: 'allocSomeData :: M Zero One T1' e' allocSomeOtherData :: M One Two T2' quindi 'do d1 <- allocSomeData; d2 <- allocSomeOtherData; return (d1, d2) :: M Zero Two (T1, T2) 'e la doppia allocazione si riflette nel tipo. – chi

risposta

18

Un functor indicizzato è, per utilizzare la dicitura spacesuitburritoesque, "un contenitore che contiene anche una mappatura". Cioè un valore f j k a "conterrà" una sorta di morfismo (s) j -> k(non necessariamente funzioni, possono essere le frecce più generali) e anche i valori di tipo a.

Per i valori a, il contenitore è un functor in modo ovvio. In effetti la classe IxFunctor da solo è piuttosto noioso - un

instance IxFunctor f 

è fondamentalmente lo stesso come

instance Functor (f j k) 

Ora, dove si fa interessante è quando si considerano le classi functor più specifici. Questo monade non è in realtà nel modulo Indexed, ma penso che rende il miglior punto chiaro:

class IxPointed f => IxMonad f where 
    ijoin :: m j k (m k l a) -> m j l a 

confronta side-by-side:

(>>>) :: (j->k) -> (k->l) -> j->l 
ijoin :: m j k (m k l a) -> m j l a 
join :: m  (m  a) -> m  a 

Quindi quello che facciamo è, mentre ci uniamo ai "container-layers", abbiamo comporre i morfismi.

L'esempio ovvio è IxState. Ricordiamo monade stato standard

newtype State s a = State { runState :: s -> (a, s) } 

Questo, quando usato come monade, compone semplicemente l'aspetto s -> s della funzione:

join (State f) = State $ \s -> let (State f', s') = f s in f' s' 

modo da infilare stato prima attraverso f, poi attraverso f'. Beh, non c'è davvero nessuna ragione per cui abbiamo bisogno che tutti gli stati abbiano lo stesso tipo, giusto? Dopo tutto, lo stato intermedio viene semplicemente passato all'azione successiva. Ecco la monade Stato indicizzato,

newtype IxState i j a = IxState { runIxState :: i -> (a, j) } 

Lo fa proprio questo:

ijoin (IxState f) = IxState $ \s -> let (IxState f', s') = f s in f' s' 
+1

Se ho capito bene, dovrebbe essere "runIxState :: i -> (a, j)", non dovrebbe? E grazie per questa spiegazione davvero buona, ora finalmente hanno senso per me ... – phg

+0

La grande nozione di "compone simultaneamente morfismi" –

Problemi correlati