سؤال
أريد إنشاء موناد الخاص بي. هذا ما كتبته:
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)
لكن هذا لا يعمل. يقول GHC:
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'
ما الخطأ فى ذلك؟
أريد إجراء حسابات بينما أنا أقل من n. يجب أن تكون ثوابت من قبل لا أعرف بعد كيفية القيام بذلك بشكل صحيح. يجب أن يكون مزيج من الدولة وربما. إذا كان لديك بعض النصائح ، فلا تتردد في مشاركتها معي: P
المحلول
عن return
:
Prelude> :t return
return :: (Monad m) => a -> m a
لذا return
يأخذ حجة من النوع a
, ويعيد شيء من النوع m a
. في هذه الحالة m
هو LeafConType
, ، لذا LeafConType a
يتم إرجاع.
لنفترض الآن أننا نمر True
. ثم a = Bool
, ، لذلك يجب أن يكون نوع العودة LeafConType Bool
. ومع ذلك ، يمكنك تحديد:
return = LeafCon
لذا، return True
يصبح LeafCon True
. لكن هذا غير مسموح به ، لأن تعريف نوع LeafConType
ينص علي
data LeafConType a = LeafCon (a, Int, Int)
وذلك ل LeafConType Bool
الحجة ل LeafCon
يجب أن يكون النوع (Bool, Int, Int)
, ، ليس مجرد Bool
. وهذا ما يعنيه خطأ التجميع: a
لا يمكن أن يكون هو نفسه (a, Int, Int)
. ذكرت:
أريد أن أقوم بحسابات أثناء
i
أقل منn
.
هذا يعني أنك ستحتاج إلى بعض القيم الافتراضية لـ i
و n
, ، وإلا سيكون من المستحيل تحديد return
. إذا كان كلاهما صفرًا بشكل افتراضي ، فيمكنك تحديد:
return a = LeafCon (a, 0, 0)
عن (>>=)
:
Prelude> :t (>>=)
(>>=) :: (Monad m) => m a -> (a -> m b) -> m b
انظر الآن إلى تطبيقك (تدوين مختلف قليلاً ، نفس الفكرة):
lc@(LeafCon (t, i, n)) >>= f | i >= n = lc
| otherwise = f t
ما نراه هنا ، هل هذا lc
يتم إرجاعه عندما i >= n
. لكن lc
من النوع LeafConType a
, ، في حين f
هي وظيفة قد ترجع قيمة النوع LeafConType b
, ، ل أي b
. نتيجة لذلك يمكن أن يكون ذلك b
لا يساوي a
وبالتالي هذه الأنواع لا تتطابق. في الختام ، عليك بجدية أن تطرح نفسك على سؤال واحد:
هل يمكن التعبير عن هذا النوع من الحساب كموناد على أي حال؟
نصائح أخرى
الوظائف التي حددتها >>=
و return
لا ترضي الأنواع المطلوبة بواسطة Monad
:
return :: a -> LeafConType a
بالنظر إلى الإعلان
return = LeafCon
أنت تعطي الوظيفة النوع غير المتوافق
return :: (a, Int, Int) -> LeafConType a
بيان مثل return 42
لذلك سيكون مستحيلًا في مونادك.
لا أفهم ما يجب أن تفعله موناد على الإطلاق. أولا إلقاء نظرة على موناد البسيطة والعمل!
instance Monad [] where
(>>=) = concatMap
return a = [a]
instance Monad Maybe where
return = Just
(Just x) >>= f = f x
Nothing >>= f = Nothing
انطلاقًا من وصفك لما تريد أن تفعله موناد ، أعتقد أنك تريد شيئًا مثل هذا:
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)
بعض الأمثلة:
*Main> runLeafCon 2 10 $ example 4
Just ((),6)
*Main> runLeafCon 2 10 $ example 8
Nothing
*Main> runLeafCon 2 10 $ example 7
Just ((),9)
لقد ألقيت هذا معًا بسرعة كبيرة ، إنه قبيح إلى حد ما ، ولم أتحقق من معرفة ما إذا كان يطيع أي من قوانين Monad ، لذا استخدم على خطرك! قون