La creación de mónadas en Haskell
Pregunta
Quiero crear mi propia mónada. Esto es lo que escribió:
data LeafConType a = LeafCon (a,Int,Int)
instance Monad (LeafConType ) where
return = LeafCon
lc@(LeafCon (t,i,n)) >>= f = if i>=n
then lc
else f (t,i,n)
Sin embargo, este trabajo no haga. GHC dice:
leafcon.hs:26:1:
Occurs check: cannot construct the infinite type: a = (a, Int, Int)
When generalising the type(s) for `return'
In the instance declaration for `Monad LeafConType'
leafcon.hs:27:1:
Occurs check: cannot construct the infinite type: a = (a, Int, Int)
When generalising the type(s) for `>>='
In the instance declaration for `Monad LeafConType'
¿Cuál es incorrecto con eso?
Quiero hacer cálculos, mientras que i es menor que n. n deben ser constantes por que aún no saben cómo hacer esto correctamente. Debe haber alguna combinación de Estado y lo mejor. Si usted tiene algunos consejos sentir a compartirlo conmigo libre: P
Solución
Acerca return
:
Prelude> :t return
return :: (Monad m) => a -> m a
Así return
toma un argumento de tipo a
, y vuelve algo del tipo m a
. En este caso es m
LeafConType
, por lo que se devuelve LeafConType a
.
Ahora supongamos que pasamos True
. Entonces a = Bool
, por lo que el tipo de retorno debe ser LeafConType Bool
. Sin embargo, se define:
return = LeafCon
Por lo tanto, se convierte en return True
LeafCon True
. Pero esto no está permitido, ya que la definición de tipo de estados que LeafConType
data LeafConType a = LeafCon (a, Int, Int)
Así que para LeafConType Bool
el argumento a LeafCon
debe tener un tipo (Bool, Int, Int)
, no sólo Bool
. Y eso es lo que significa el error de compilación: a
no pueden ser los mismos que (a, Int, Int)
. Usted Estado:
Quiero hacer cálculos mientras
i
es menor quen
.
Esto significa que se necesitarán algunos valores por defecto para i
y n
, de lo contrario será imposible definir return
. Si ambos son cero por defecto, entonces se podría definir:
return a = LeafCon (a, 0, 0)
Acerca (>>=)
:
Prelude> :t (>>=)
(>>=) :: (Monad m) => m a -> (a -> m b) -> m b
Ahora mira a su aplicación (notación ligeramente diferente, la misma idea):
lc@(LeafCon (t, i, n)) >>= f | i >= n = lc
| otherwise = f t
Lo que vemos aquí, es que se devuelve cuando lc
i >= n
. Pero lc
es de tipo LeafConType a
, mientras f
es una función que puede devolver un valor de tipo LeafConType b
, por cualquier b
. Como resultado de ello podría ser que b
no es igual a a
y por lo tanto estos tipos no coinciden. En conclusión, en serio tiene que hacerse una pregunta:
¿Puede este tipo de cálculo puede expresar como una mónada de todos modos?
Otros consejos
Las funciones que ha indicado para >>=
y return
no satisfacen los tipos requerida por Monad
:
return :: a -> LeafConType a
Teniendo en cuenta la declaración
return = LeafCon
le da la función del tipo incompatible
return :: (a, Int, Int) -> LeafConType a
Una declaración como return 42
por lo tanto, sería imposible en su mónada.
No entiendo lo que su mónada debe hacerlo en absoluto. En primer lugar echar un vistazo a simple, mónadas de trabajo!
instance Monad [] where
(>>=) = concatMap
return a = [a]
instance Monad Maybe where
return = Just
(Just x) >>= f = f x
Nothing >>= f = Nothing
A juzgar por su descripción de lo que usted quiere que su mónada hacer, creo que quiere algo un poco como esto:
data LeafConType a = LeafCon { runLeafCon' :: Int -> Int -> (Maybe a, Int, Int) }
runLeafCon :: Int -> Int -> LeafConType a -> Maybe a
runLeafCon i n lc = let (t, _, _) = runLeafCon' lc i n in t
getI :: LeafConType Int
getI = LeafCon $ \i n -> (Just i, i, n)
getN :: LeafConType Int
getN = LeafCon $ \i n -> (Just n, i, n)
setI :: Int -> LeafConType ()
setI i = LeafCon $ \_ n -> (Just (), i, n)
setN :: Int -> LeafConType ()
setN n = LeafCon $ \i _ -> (Just (), i, n)
instance Monad LeafConType where
return t = LeafCon $ \i n -> if (i < n)
then (Just t, i, n)
else (Nothing, i, n)
(LeafCon k) >>= f =
LeafCon $ \i n ->
let (t, i', n') = k i n
in case t of
Nothing -> (Nothing, i', n')
(Just t') -> if (i' < n')
then runLeafCon' (f t') i' n'
else (Nothing, i, n)
example :: Int -> LeafConType ((), Int)
example x = do
i <- getI
m <- setI (i + x)
return (m, i + x)
Algunos ejemplos:
*Main> runLeafCon 2 10 $ example 4
Just ((),6)
*Main> runLeafCon 2 10 $ example 8
Nothing
*Main> runLeafCon 2 10 $ example 7
Just ((),9)
Me tiró esto junto con bastante rapidez, es bastante feo, y no he comprobado para ver si obedece a ninguna de las leyes de las mónadas, por lo que su uso bajo su propio riesgo! :)