我将在底部用示例重申我的问题。
当我说m
或k
时,我指的是函数调用中的左右值,m >>= k
.
对monads的理解是模糊的。如果m
是一个计算,k
是一个lambda表达式,这是否意味着k
有两个目的,它可以使用它来处理do块中的绑定,也可以用于Monad实例定义中的其他目的?如果一个值在其 do 块中被评估之前k
绑定,值会自动传递给它,还是 Monad 实例方法定义,我们在其中定义了传播到k
的唯一效果?
我看到的例子可能有点误导,这些例子在解释 monads 时总是将直接的前一个绑定项传递给 lambda 表达式。即使使用的符号是纯 do-no-lambda,它是否始终是传递给隐藏的 lambda 的最后一个绑定项,在引擎盖下。现在,使用接受多个参数的 lambda 并将其用作m >>= k
中的k
是一种不好的做法吗?或者我假设如果我们在一个 do 块中工作,在引擎盖下,只有一个参数传递给下一个"隐藏"的 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
的IO
容器中的值被提取并传递给 lambda 表达式。
如果第一个是正确的,那不会导致错误,因为a
在 monad 定义中是未定义的?
我凭空拉出了第二个。我没有看到任何证据表明可能是这样的,但即使它不是正确的答案,我们能做这样的事情吗?当然,在这种情况下,我们必须使所有与>>=
一起使用的 lambda 表达式都采用 2 元组。这种行为不完全是由我们的>>=
定义决定的,至少在使用 only-lambda-no-do 表示法时是这样?
我们可以在 monad 定义中使用此 lambda 表达式并向它传递一些内容,使其再次打印屏幕吗?我们应该吗?
如果我们想在>>=
定义中使用putStrLn
,我们是否必须向k
传递一些任意值才能得到putStrLn
函数?
谢谢。和平。
do
这样的表示法:
do
a <- getLine
b <- getLine
putStrLn $ a ++ b
相当于
getLine >>= a -> getLine >>= b -> putStrLn (a ++ b)
这又相当于
getLine >>= (a -> getLine >>= b -> putStrLn (a ++ b))
请注意额外的括号。第一个getLine
由一个 lambda 表达式组成,其中a
是"包含的元素"。在此 lambda 表达式中,将调用一个新表达式。该表达式是
getLine >>= b -> putStrLn (a ++ b)
此表达式仍然"在"第一个 lambda 表达式内,这意味着a
仍在范围内。
如果有帮助,您可以在表达式周围添加更多括号:
getLine >>= (a -> getLine >>= (b -> putStrLn (a ++ b)))
这些括号是完全多余的,但它们突出显示了各种表达式的作用域。请注意,调用putStrLn (a ++ b)
时,a
和b
仍在范围内。
我只会给你一个部分答案,因为我不明白,你到底在问什么。对不起。
do
a <- getLine
b <- getLine
putStrLn $ a ++ b
相当于
getLine >>= a -> getLine >>= b -> putStrLn (a ++ b)
这有效,因为
b -> putStrLn (a ++ b)
创建一个函数,该函数从定义的范围内捕获a
在。如果 在此术语范围内没有此类a
,编译器将抱怨a
不在范围内。
让我们稍微扩展一下,看看
a -> getLine >>= b -> putStrLn (a ++ b)
再。这定义了一个函数,如果应用于某个值x
则返回表达式的结果
getLine >>= b -> putStrLn (x ++ b)
在这里,变量a
被值x
替换。因此,无需再担心a
。
无糖do block
就像getLine >>= a -> getLine >>= b -> putStrLn (a ++ b)
由于本地作用域而不需要传递先前调用的所有参数,让我们表示它们:
getLine >>=
(!This is first closure! a -> getLine >>=
(!This is second, all variables from the first
closure are available, because in haskell function closure takes outer scope in! b ->
putStrLn(a ++ b) !End of the second closure!) !End of the first closure!)
现在关于 (>>=) 的类型。GHCI打印下一个(>>=) :: Monad m => m a -> (a -> m b) -> m b
. 所以k
只是一个Monad m
将使用的lambda,它所要做的就是为>>=
"实现接口"。
do
是否等同于以下内容?getLine >>= a -> getLine >>= b -> putStrLn (a ++ b)
是的,这个。
如果第一个是正确的,那不会导致错误,因为在 monad 定义中未定义 a?
不。->
箭头语法是右关联的:
getLine >>= (a -> getLine >>= (b -> putStrLn (a ++ b)))
它形成一个闭包,a
仍在范围内。