Question
Je veux créer ma propre monade. C'est ce que je l'ai écrit:
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)
Mais ce travail de Do not. GHC dit:
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'
Quel est le problème avec ça?
Je veux faire des calculs alors que i est inférieur à n. n doit être constantes par je ne sais pas encore comment faire correctement. Il devrait y avoir un mélange d'Etat et peut-être. Si vous avez des conseils ne hésitez pas à partager avec moi: P
La solution
A propos de return
:
Prelude> :t return
return :: (Monad m) => a -> m a
return
prend un argument de type a
et renvoie quelque chose de type m a
. Dans ce cas m
est LeafConType
, donc LeafConType a
est retourné.
Supposons maintenant que nous passons True
. Ensuite a = Bool
, de sorte que le type de retour doit être LeafConType Bool
. Cependant, vous définissez:
return = LeafCon
Alors, return True
devient LeafCon True
. Mais ce n'est pas autorisé, parce que la définition de type de LeafConType
indique que
data LeafConType a = LeafCon (a, Int, Int)
Donc, pour LeafConType Bool
l'argument LeafCon
doit avoir le type (Bool, Int, Int)
, pas seulement Bool
. Et c'est-ce que l'erreur de compilation signifie: a
ne peut pas être le même que (a, Int, Int)
. Vous déclarez:
Je veux faire des calculs en
i
est inférieure àn
.
Cela signifie que vous aurez besoin de quelques valeurs par défaut pour i
et n
, car autrement il sera impossible de définir return
. Si les deux d'entre eux sont zéro par défaut, vous pouvez alors définir:
return a = LeafCon (a, 0, 0)
A propos de (>>=)
:
Prelude> :t (>>=)
(>>=) :: (Monad m) => m a -> (a -> m b) -> m b
Maintenant, regardez votre implémentation (notation légèrement différente, même idée):
lc@(LeafCon (t, i, n)) >>= f | i >= n = lc
| otherwise = f t
Ce que nous voyons ici, est que lc
est retourné lorsque i >= n
. Mais lc
est de type LeafConType a
, alors que f
est une fonction qui peut renvoyer une valeur de type LeafConType b
, pour any b
. Par conséquent, il se pourrait que b
ne correspond pas à a
et par conséquent, ces types ne correspondent pas. En conclusion, vous avez sérieusement vous poser une question:
Peut ce type de calcul exprimé comme une monade de toute façon?
Autres conseils
Les fonctions que vous avez spécifié pour >>=
et return
ne satisfont pas les types requis par Monad
:
return :: a -> LeafConType a
Compte tenu de la déclaration
return = LeafCon
vous donnez la fonction du type incompatible
return :: (a, Int, Int) -> LeafConType a
Une déclaration comme return 42
serait donc impossible dans votre monade.
Je ne comprends pas ce que votre monade devrait faire du tout. Tout d'abord jeter un oeil à simple, monades travail!
instance Monad [] where
(>>=) = concatMap
return a = [a]
instance Monad Maybe where
return = Just
(Just x) >>= f = f x
Nothing >>= f = Nothing
A en juger par votre description de ce que vous voulez que votre monade faire, je pense que vous voulez quelque chose un peu comme ceci:
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)
Voici quelques exemples:
*Main> runLeafCon 2 10 $ example 4
Just ((),6)
*Main> runLeafCon 2 10 $ example 8
Nothing
*Main> runLeafCon 2 10 $ example 7
Just ((),9)
Je jeté cela ensemble assez rapidement, il est assez laid, et je n'ai pas vérifié pour voir si elle obéit à l'une des lois Monad, utilisez donc à vos risques et périls! :)