Erstellen von Monaden in Haskell
Frage
Ich möchte mein eigenes Monade erstellen. Das ist, was ich schrieb:
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)
Aber das tun sie nicht arbeiten. GHC sagt:
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'
Was ist daran falsch?
Ich möchte Berechnungen tun, während ich niedriger als n ist. n sollte ich sein Konstanten wissen noch nicht, wie man diese richtig zu tun. Es sollte eine gewisse Mischung aus Staat und vielleicht sein. Wenn Sie einige Ratschläge haben, fühlen sich frei, es mit mir zu teilen: P
Lösung
Über return
:
Prelude> :t return
return :: (Monad m) => a -> m a
So return
nimmt ein Argument vom Typ a
und kehrt etwas vom Typ m a
. In diesem Fall m
ist LeafConType
, so LeafConType a
zurückgeführt wird.
Nehmen wir nun an, dass wir True
passieren. Dann a = Bool
, so muss der Rückgabetyp LeafConType Bool
sein. Allerdings definieren Sie:
return = LeafCon
So wird return True
LeafCon True
. Aber das ist nicht erlaubt, weil die Typdefinition von LeafConType
besagt, dass
data LeafConType a = LeafCon (a, Int, Int)
Also für LeafConType Bool
das Argument LeafCon
muss Typ (Bool, Int, Int)
haben, nicht nur Bool
. Und das ist, was die Compiler-Fehler bedeutet: a
nicht die gleiche wie (a, Int, Int)
sein kann. Sie Zustand:
Ich möchte Berechnungen tun, während
i
niedriger alsn
ist.
Das bedeutet, dass Sie einige Standardwerte für i
und n
benötigen, denn sonst wird es unmöglich sein return
zu definieren. Wenn beide auf Null gesetzt sind, dann könnte man definieren:
return a = LeafCon (a, 0, 0)
Über (>>=)
:
Prelude> :t (>>=)
(>>=) :: (Monad m) => m a -> (a -> m b) -> m b
Überprüfen Sie nun Ihre Implementierung (etwas andere Schreibweise, gleiche Idee):
lc@(LeafCon (t, i, n)) >>= f | i >= n = lc
| otherwise = f t
Was wir hier sehen ist, dass lc
zurückgegeben wird, wenn i >= n
. Aber lc
ist vom Typ LeafConType a
, während f
eine Funktion ist, die einen Wert vom Typ LeafConType b
zurückgeben kann, für jeder b
. Als Ergebnis könnte es sein, dass b
zu a
nicht gleich ist und daher diese Arten nicht übereinstimmen. Abschließend müssen Sie ernsthaft selbst eine Frage stellen:
Kann diese Art der Berechnung als Monade ohnehin ausgedrückt werden?
Andere Tipps
Die Funktionen, die Sie für >>=
und return
angegeben nicht die Typen erfüllen erforderlich durch Monad
:
return :: a -> LeafConType a
In Anbetracht der Erklärung
return = LeafCon
Sie geben die Funktion, die nicht kompatiblen Typ
return :: (a, Int, Int) -> LeafConType a
Eine Aussage wie return 42
wäre daher in der Monade unmöglich sein.
Ich verstehe nicht, was Ihr Monade überhaupt tun sollte. Zuerst nehmen Sie einen Blick auf einfache, Arbeits Monaden!
instance Monad [] where
(>>=) = concatMap
return a = [a]
instance Monad Maybe where
return = Just
(Just x) >>= f = f x
Nothing >>= f = Nothing
Geht man von Ihrer Beschreibung dessen, was Sie möchten, dass Ihre Monade zu tun, ich glaube, Sie etwas ein bisschen wie diese wollen:
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)
Einige Beispiele:
*Main> runLeafCon 2 10 $ example 4
Just ((),6)
*Main> runLeafCon 2 10 $ example 8
Nothing
*Main> runLeafCon 2 10 $ example 7
Just ((),9)
Ich warf diese zusammen ziemlich schnell, es ist ziemlich hässlich, und ich habe nicht überprüft, um zu sehen, ob es eine der Monad Gesetze gehorcht, so Nutzung auf eigene Gefahr! :)