2010-08-20 11 views
12

Dopo aver letto (e sfogliato alcune sezioni di) il lavoro di Wadler sulle monadi, ho deciso di lavorare più a fondo sul foglio, definendo il funtore e le istanze applicative per ciascuna delle monadi che descrive. Utilizzando il tipo sinonimoFunctor/istanze applicative per stato in Haskell

type M a = State -> (a, State) 
type State = Int 

Wadler utilizza per definire la monade stato, ho i seguenti (nomi correlati che utilizzano in modo che possa definirli con una dichiarazione newtype in seguito).

fmap' :: (a -> b) -> M a -> M b 
fmap' f m = \st -> let (a, s) = m st in (f a, s) 

pure' :: a -> M a 
pure' a = \st -> (a, st) 

(<@>) :: M (a -> b) -> M a -> M b 
sf <@> sv = \st -> let (f, st1) = sf st 
         (a, st2) = sv st1 
        in (f a, st2) 

return' :: a -> M a 
return' a = pure' a 

bind :: M a -> (a -> M b) -> M b 
m `bind` f = \st -> let (a, st1) = m st 
         (b, st2) = f a st1 
        in (b, st2) 

Quando si passa ad usare un costruttore di tipo in una dichiarazione newtype, ad esempio,

newtype S a = S (State -> (a, State)) 

tutto crolla. Tutto è solo una leggera modifica, per esempio,

instance Functor S where 
fmap f (S m) = S (\st -> let (a, s) = m st in (f a, s)) 

instance Applicative S where 
pure a = S (\st -> (a, st)) 

tuttavia nulla viene eseguito in GHC a causa del fatto che l'espressione lambda è nascosto all'interno di quel tipo di costruzione. Ora l'unica soluzione che vedo è quello di definire una funzione:

isntThisAnnoying s (S m) = m s 

allo scopo di impegnare s per 'st' ed effettivamente restituire un valore, ad esempio,

fmap f m = S (\st -> let (a, s) = isntThisAnnoying st m in (f a, s)) 

c'è un altro modo per fare questo che non usa queste funzioni ausiliarie?

risposta

11

Se si guarda here, si vedrà che essi definiscono in questo modo:

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

in modo da dare il lambda interno un nome.

+2

Che significa anche 'runState = flip isntThisAnnoying'. – kennytm

+1

OK - così mentre è ancora necessaria una funzione ausiliaria, potrei semplicemente definire il tipo usando un record, ottenendo la funzione gratuitamente. Quello che stai dicendo, quindi, è che non c'è modo di evitare l'uso di funzioni come "runState" o "run". Grazie. – danportin

+0

Se ti fa male pensarlo come una funzione, pensa invece ad esso come un accessore di campo struct. :-) –

4

Il modo usuale è definire newtype newtype S a = S {runState : State -> (a, State)}. Quindi, invece del tuo isntThisAnnoying s (S m) puoi scrivere runState t s dove t è lo stesso di S m.
È necessario utilizzare un newtype perché i sinonimi di tipo non possono essere istanze di typeclass.