Haskell总是知道要回电话吗?

时间:2011-10-20 19:47:39

标签: haskell monads

我正在定义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中吗?

3 个答案:

答案 0 :(得分:16)

这里有一些大问题。

首先,Monad个实例必须kind * -> *。这意味着他们至少需要一个类型变量,其中Something没有任何变量。为了比较:

-- kind * -> *
Maybe
IO
Either String

-- kind *
Maybe Int
IO ()
Either String Double

在使用之前,了解MaybeIOEither 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。SomethingIdentity 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)

  1. 关闭。这里return是多余的,您需要在Something类型构造函数中添加一个类型参数。 编辑:此代码仍然有问题。 >>=的定义是循环的。有关详细信息,请参阅其他答案。

    data Something a = Something a
    
    instance Monad Something where
        return a = Something a
        m >>= f = do
            var <- m
            f var
    
  2. 由于您对>>=的定义位于instance Monad Something where下,>>=的类型为Something a -> (a -> Something b) -> Something b。因此,它可以告诉f var必须是Something b类型。谷歌这里的术语是“类型推断”

  3. 是。同样,这是类型推断。如果编译器无法推断出您想要的类型,它会告诉您。但通常它可以。