我正在定义monad的实例,如下所示:
data Something = Something a
instance Monad Something where
return a = Something a --Wraps a in 'Something', correct?
m >>= f = do
var <- m
return $ f var --I want this to pass var to f, then wrap the result
--back up in the 'Something' monad with the return I
--Just defined
问题是 - &gt;
1:我正在做的事情是否有任何明显的错误/误解?
2:Haskell是否知道从m >>= f
3:如果我出于某种原因定义了另一个功能
f :: Something a -> Something b
f x = do
var <- x
return $ doMagicTo x
return
会调用我在monad实例中定义的返回值并将{x}包裹在Something
中吗?
答案 0 :(得分:16)
这里有一些大问题。
首先,Monad
个实例必须kind * -> *
。这意味着他们至少需要一个类型变量,其中Something
没有任何变量。为了比较:
-- kind * -> *
Maybe
IO
Either String
-- kind *
Maybe Int
IO ()
Either String Double
在使用之前,了解Maybe
,IO
和Either String
中的每一个如何需要类型参数?使用Something
,类型参数无法填写。因此您需要将定义更改为:
data Something a = Something a
第二个大问题是Monad实例中的>>=
是错误的。您通常无法使用do-notation,因为它只调用Monad
函数return
和>>=
。因此,您必须在没有任何monad函数的情况下将其写出来,或者使用do-notation或者调用>>=
或return
。
instance Monad Something where
return a = Something a --Wraps a in 'Something'
(Something m) >>= f = f m --unwraps m and applies it to f
>>=
的定义比您预期的简单。展开m
非常简单,因为您只需要在Something
构造函数上进行模式匹配。同样f :: a -> m b
,因此您无需担心再次将其包装起来,因为f
会为您做到这一点。
虽然没有办法解开一般的monad ,但很多特定的 monad都可以解包。
请注意,在monad实例声明中使用do-notation或>>=
在语法上没有任何错误。问题是>>=
是递归定义的,所以当你尝试使用它时,程序会进入无限循环。
(此处定义的N.B。Something
是Identity monad)
对于第三个问题,是Monad实例中定义的return
函数是将要调用的函数。类型类按类型调度,并且如您指定的类型必须为Something b
,编译器将自动将Monad实例用于Something
。 (我想你的最后一行是doMagicTo var
)。
答案 1 :(得分:8)
主要问题是您对>>=
的定义是循环的。
Haskell的do语法是>>=
和>>
链的合成糖,所以你的定义
m >>= f = do
var <- m
return $ f var
Desugars to
m >>= f =
m >>= \var ->
return $ f var
因此,您将m >>= f
定义为m >>= \...
,这是循环的。
您需要对>>=
执行的操作是定义如何从m中提取值以传递给f
。此外,您的f
应该返回一个monadic值,因此在这里使用return
是错误的(这更接近于您定义fmap
的方式)。
>>=
Something
的定义可能是:
(Something a) >>= f = f a
这是身份Monad - 有很多关于它的文章 - 它是理解monad如何工作的一个很好的起点。
答案 2 :(得分:2)
关闭。这里return
是多余的,您需要在Something
类型构造函数中添加一个类型参数。 编辑:此代码仍然有问题。 >>=
的定义是循环的。有关详细信息,请参阅其他答案。
data Something a = Something a
instance Monad Something where
return a = Something a
m >>= f = do
var <- m
f var
由于您对>>=
的定义位于instance Monad Something where
下,>>=
的类型为Something a -> (a -> Something b) -> Something b
。因此,它可以告诉f var
必须是Something b
类型。谷歌这里的术语是“类型推断”
是。同样,这是类型推断。如果编译器无法推断出您想要的类型,它会告诉您。但通常它可以。