我将用底部的例子重述我的问题 .
当我说 m
或 k
时,我指的是函数调用中的左右值, m >>= k
.
对单子的理解有这种模糊 . 如果 m
是一个计算而且 k
是一个lambda表达式,那是否意味着 k
有两个目的,它可以使用do块中的do块中的绑定,也可以用于Monad实例定义中的其他目的?如果在其do块中对 k
进行求值之前绑定了一个值,那么值是自动传递给它还是Monad实例方法定义,我们定义传播到 k
的唯一影响?
我可能有点误导我看到的例子,在解释monad时总是将前一个绑定项直接传递给lambda表达式 . 即使使用的符号是pure-do-no-lambda,它总是最后一个绑定的项目传递给隐藏的lambda,在引擎盖下 . 现在,使用一个带有多个参数的lambda并在 m >>= k
中将其用作 k
是不好的做法吗?或者我错误地假设如果我们在do块中工作,那么只有一个参数传递给下一个'hidden' lambda表达式并且这个参数是先前绑定的项目?
我现在将用例子重述我的问题 .
do
a <- getLine
b <- getLine
putStrLn $ a ++ b
a
和 b
绑定到从运行 getLine
返回的 IO
容器中的值 . 在引擎盖下,以下哪一项是相同的,如果有的话?
getLine >>= \a -> getLine >>= \b -> putStrLn (a ++ b)
要么
getLine >>= \a -> getLine >>= \(a, b) -> putStrLn (a ++ b)
我们看到 getLine
_容器中 getLine
的值被提取并传递给lambda表达式 .
如果第一个是正确的,那么不会导致错误,因为在monad定义中 a
将是未定义的吗?
凭空我拉了第二个 . 我是正确的答案,我们可以这样做吗?当然,我们必须使用 >>=
的所有lambda表达式都在这个上下文中使用2元组 . 这种行为不仅仅是由我们的 >>=
定义决定的,至少在使用lambda-no-do表示法时是这样吗?
我们可以在monad定义中使用这个lambda表达式并将一些东西传递给它,导致它再次打印屏幕吗?我们应该吗?
如果我们想在 >>=
定义中使用 putStrLn
,我们是否必须将一些任意值传递给 k
才能获得 putStrLn
函数?
谢谢 . 和平 .
4 回答
do
符号如下:相当于
这再次等同于
注意额外的括号 . 第一个
getLine
由一个lambda表达式组成,其中a
是'contained element' . 在这个lambda表达式中,调用了一个新表达式 . 那个表达是此表达式仍然是'inside'第一个lambda表达式,这意味着
a
仍在范围内 .如果它有帮助,你可以在表达式周围添加更多括号:
这些括号完全是多余的,但它们突出了各种表达式的范围 . 请注意,调用
putStrLn (a ++ b)
时,a
和b
仍在范围内 .我只会给你一个部分答案,因为我不明白,你究竟在问什么 . 对不起 .
相当于
这是有效的,因为
创建一个函数,它从定义的范围中捕获
a
. 如果在这个术语的范围内不是a
,编译器会抱怨a
不在范围内 .让我们稍微扩展一下并看一看
再次 . 这定义了一个函数,如果应用于某个值,
x
将返回表达式的结果这里变量
a
被值x
替换 . 所以不用再担心了.1122733_ .没有用
do block
就像getLine >>= \a -> getLine >>= \b -> putStrLn (a ++ b)
你不是't need to pass all parameters from previous call because of local scopes, let' s表示它们:现在关于类型(>> =) . GHCI打印下一个
(>>=) :: Monad m => m a -> (a -> m b) -> m b
. 所以k
只是Monad m
将使用的lambda,所有它必须做的是"implement interface"为>>=
.是的,这个 .
不,
->
箭头语法是右关联:它形成一个闭包,其中
a
仍然在范围内 .