正确使用符号

时间:2011-11-01 05:44:52

标签: haskell monads

我想了解Monad,我有以下代码

f a b c d =
   do one <- a + b
      two <- c * d
      three <- one + two
      return three

以上编译

但是当我

时出错
*Main> f 1 2 3 4

:1:1:
    No instances for (Num (a0 -> t0), Monad ((->) a0), Monad ((->) t0))
      arising from a use of `f'
    Possible fix:
      add instance declarations for
      (Num (a0 -> t0), Monad ((->) a0), Monad ((->) t0))
    In the expression: f 1 2 3 4
    In an equation for `it': it = f 1 2 3 4

:1:9:
    No instance for (Num (a0 -> a0 -> t0))
      arising from the literal `4'
    Possible fix:
      add an instance declaration for (Num (a0 -> a0 -> t0))
    In the fourth argument of `f', namely `4'
    In the expression: f 1 2 3 4
    In an equation for `it': it = f 1 2 3 4

如果我知道为什么上面的代码不适用于我,我想我会更接近理解Monad f 1 2 3 4

5 个答案:

答案 0 :(得分:8)

问题是你用纯值混淆包裹的monadic值。

首先要知道的是,符号是常规函数调用的语法糖(>>=>>)。因此,看看你的代码也会有所帮助。

让我们尝试更简单的事情

 f a b =
   do one <- a + b
      return one

这与您的代码有同样的问题,但更简单。要理解为什么它不起作用,我们会问:这实际意味着什么?好吧,我们可以使用<-

重写>>=符号
 f a b = (a + b) >>= \x -> return x

(这不是最简单的表示,但明确指出)

如果您在GHCi中测试以下内容

 >> :t (>>=)
 Monad m => m a -> (a -> m b) -> m b

即,函数>>=采用:m的{​​{1}}类型的参数和a的{​​{1}}到a的函数}并返回m b

这段代码怎么样?

m

将成为一个数字。 另一半怎么样

b

获取(a + b) 类型的对象,并为任何 \x -> return x

返回a类型的对象

所以,你需要有一个数字,也就是某种形式的monad。你能想到这样的事吗?目前尚不清楚这将是什么,这是一个怀疑这应该打字的原因。

与monad达成协议的一个好方法是查看一些具体的例子。

m a monad表示可能失败的计算

a

这可以让你用类似

的模式说话
Maybe

或更简单的相同代码

 instance Monad Maybe where
      return = Just
      (>>=) (Just a) f = f a
      (>>=) Nothing _ = Nothing

或者

 f args = do x <- functionThatMightFail args
             y <- anotherfunctionThatMightFail x
             return y

另一方面,f args = do x <- functionThatMightFail args anotherfunctionThatMightFail x monad捕获了对列表的每个元素执行相同功能,然后将结果连接在一起的想法。简单的例子比比皆是:

f args = functionThatMightFail args >>= anotherfunctionThatMightFail

如果您了解这些,请使用List monad。它可以帮助您更全面地了解“monad是计算模型”。然后我会检查Parsec,当然还有IO

答案 1 :(得分:8)

我不同意其他人的意见并说出你所做的几乎肯定只是与monads无关。您可能只想使用这样一些无聊的旧代码:

f a b c d = three where
    one = a + b
    two = c * d
    three = one + two

或更简洁:

f a b c d = a + b + c * d

答案 2 :(得分:3)

我认为你对<-的用途感到困惑。该运算符从monad中“解包”数据;在这种情况下IO monad。例如,如果您调用readLn,则会收到类型IO a的结果。如果您想在monad中使用a,可以将input <- readLn放在do构造中,这会将a的值绑定到input 。但是,a + b的值不在IO monad或任何monad中。在您的示例使用中,a + b只是类型Int。因此,如果要声明一个等于该变量的变量,则使用let语句:let one = a + b。您的其他声明也是如此。因此,您需要将此函数重写为:

f a b c d = do
    let one = a + b
    let two = c * d
    let three = one + two
    return three

另外需要注意:添加类型签名通常可以帮助调试此类事情。如果我将此类型签名添加到您的原始函数f :: Int -> Int -> Int -> Int -> IO Int,我会收到一个更有帮助的错误,该错误表示期望的类型为IO t0,但实际类型为Int在表达式{ {1}}。这可以帮助您意识到one <- a + b期望monadic值作为其右侧参数,但它收到了<-

答案 3 :(得分:1)

你想用哪个Monad?

与您尝试做的最接近的匹配是函数monad ((->) a)(因此GHC错误),但是当您在每个阶段提供两个参数时,这不起作用。

不要试图通过将代码敲入do-blocks来理解Monads:了解如何使用特定的Monad(例如学习如何使用IO,学习如何使用monadic解析库等),以及然后了解他们的共性以及Monad抽象的工作原理。

每个Monad都有自己独特的特点和工作方法(因为如果他们没有,那么拥有多个人的重点是什么?)。

我特别喜欢Real World Haskell对此的处理方法:各种Monad都引入了辅助组合器,以帮助管理样板,然后在Chapter 7中将所有组合在一起并正式引入Monad类型类。

答案 4 :(得分:1)

如果您尝试使用Maybe monad进行错误处理,请参阅以下示例:

module Bbb where

import Data.Maybe

f a b c d =
   do one <- return $ a + b
      two <- return $ c * d
      three <- return $ one + two
      return three

main = print $ fromJust $ f 1 2 3 4

import Data.Maybe子句适用于fromJust,只是

fromJust (Just x) = x

如果您只想将<-解释为作业,请不要:)