我想了解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
答案 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
如果您只想将<-
解释为作业,请不要:)