Haskell总是知道要回电话吗?

pro*_*nce 8 monads haskell

我正在定义一个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
Run Code Online (Sandbox Code Playgroud)

问题是 - >

1:我在做什么有明显的错误/误解吗?

2:Haskell会知道调用我上面定义的返回 m >>= f

3:如果由于某种原因我定义了另一个功能

f :: Something a -> Something b
f x = do
    var <- x
    return $ doMagicTo x
Run Code Online (Sandbox Code Playgroud)

return调用我在monad实例中定义的返回并将x包装进去Something

Joh*_*n L 16

这里有一些大问题.

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

-- kind * -> *
Maybe
IO
Either String

-- kind *
Maybe Int
IO ()
Either String Double
Run Code Online (Sandbox Code Playgroud)

看到每一个Maybe,IO以及Either String需要一个类型参数,然后才能使用它们?有了Something,没有地方可以填写类型参数.所以你需要将你的定义更改为:

data Something a = Something a
Run Code Online (Sandbox Code Playgroud)

第二个大问题是>>=你的Monad实例中的错误.你通常不能使用do-notation,因为它只调用Monad函数return>>=.所以你必须在没有任何monad函数的情况下写出来,无论是do-notation还是call >>=或者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
Run Code Online (Sandbox Code Playgroud)

定义>>=比您预期的简单.展开m很容易,因为你只需要在Something构造函数上进行模式匹配.另外f :: a -> m b,所以你不必担心再把它包起来,因为f这样做对你而言.

虽然一般没有办法打开monad ,但很多特定的 monad都可以打开.

请注意,使用do-notation或>>=monad实例声明在语法上没有任何错误.问题是以>>=递归方式定义,因此当您尝试使用它时,程序会进入无限循环.

(Something这里定义的NB 是Identity monad)

对于第三个问题,是returnMonad实例中定义的函数是将被调用的函数.类型按类型调度,并且如您所指定的那样,类型必须是Something b编译器将自动使用Monad实例Something.(我想你的意思是最后一行doMagicTo var).

  • 这很棒.所有这些答案都非常好.我不得不说,到目前为止,Haskell社区是我遇到的最有帮助和最彻底的.干杯伙计们. (3认同)

ram*_*ion 8

主要问题是你的定义>>=是循环的.

Haskell的做语法是链语法糖>>=>>,让您的定义

m >>= f = do
    var <- m
    return $ f var         
Run Code Online (Sandbox Code Playgroud)

Desugars来

m >>= f = 
    m >>= \var -> 
    return $ f var
Run Code Online (Sandbox Code Playgroud)

所以你定义m >>= fm >>= \...,这是循环的.

您需要做的>>=是定义如何从m中提取值以传递给f.另外,你f应该返回一个monadic值,所以return在这里使用是错误的(这更接近你定义的方式fmap).

>>=for 的定义Something可以是:

(Something a) >>= f = f a
Run Code Online (Sandbox Code Playgroud)

这是身份Monad - 有很多关于它的文章 - 它是了解monad如何工作的一个很好的起点.