2010-08-20 16 views
12

Después de leer (y pasar algunas secciones de) el documento de Wadler sobre mónadas, decidí trabajar más detenidamente en el documento, definiendo el funtor y las instancias aplicativas para cada una de las mónadas que describe. Que utiliza el sinónimo de tipoFunctor/Instancias aplicables para estado en Haskell

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

Wadler utiliza para definir la mónada estado, tengo los siguientes (utilizando nombres relacionados para que pueda definirlos con una declaración newtype más adelante).

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) 

Cuando cambio al uso de un constructor de tipos en una declaración newtype, por ejemplo,

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

todo se desmorona. Todo es simplemente una ligera modificación, por ejemplo,

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)) 

sin embargo nada se ejecuta en GHC debido al hecho de que la expresión lambda se oculta dentro de ese tipo constructor. Ahora, la única solución que veo es definir una función:

isntThisAnnoying s (S m) = m s 

a fin de obligar s a 'st' y en realidad devolver un valor, por ejemplo,

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

¿Hay otra manera de hacer esto que no usa estas funciones auxiliares?

Respuesta

11

Si nos fijamos here, se verá que lo definen así:

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

a fin de dar la lambda interior un nombre.

+2

Lo que también significa 'runState = flip isntThisAnnoying'. – kennytm

+1

Bien, entonces, aunque todavía se necesita una función auxiliar, podría simplemente definir el tipo usando un registro, obteniendo la función de forma gratuita. Lo que estás diciendo, entonces, es que no hay forma de evitar el uso de funciones como 'runState' o 'run'. Gracias. – danportin

+0

Si le duele pensar que es una función, piense en ello como un acceso de campo de estructura. :-) –

4

La forma habitual es definir newtype newtype S a = S {runState : State -> (a, State)}. Entonces, en lugar de su isntThisAnnoying s (S m), puede escribir runState t s donde t es lo mismo que S m.
Tiene que usar un newtype porque los sinónimos de tipo no pueden ser instancias de clase de tipo.

Cuestiones relacionadas