我将在底部以示例重述我的问题。
当我说m
或k
时,我指的是函数调用中的左右值m >>= k
。
对于单子的理解有这种模糊。如果m
是计算而k
是lambda表达式,那么这是否意味着k
有两个目的,它可以使用do块中的绑定工作,也可以用于某些Monad实例定义中的其他用途?如果在其do块中评估k
之前绑定了一个值,那么值是自动传递给它还是Monad实例方法定义,我们定义传播到k
的唯一影响?
我可能有点误导我看到的例子,在解释monad时总是将直接前一个绑定项传递给lambda表达式。即使使用的符号是pure-do-no-lambda,它总是最后一个被绑定的项目传递给隐藏的lambda,在引擎盖下。现在,使用一个带有多个参数的lambda并在k
中将其用作m >>= k
是不好的做法?或者我错误地认为,如果我们在一个do块中工作,那么只有一个参数传递给下一个隐藏的' lambda表达式,这一个参数是以前绑定的项目吗?
我现在将用例子重述我的问题。
do
a <- getLine
b <- getLine
putStrLn $ a ++ b
a
和b
绑定到IO
容器中从运行getLine
返回的值。在引擎盖下,以下哪一项是相同的,如果有的话?
getLine >>= \a -> getLine >>= \b -> putStrLn (a ++ b)
OR
getLine >>= \a -> getLine >>= \(a, b) -> putStrLn (a ++ b)
我们看到IO
getLine
容器中的值被提取并传递给lambda表达式。
如果第一个是正确的,那么会导致错误,因为monad定义中a
未定义?
我凭空掏出第二个。我没有看到它可能是这样的证据,但即使它不是正确的答案,我们可以这样做吗?当然,我们必须使用>>=
的所有lambda表达式都在这个上下文中使用2元组。这种行为不仅仅由我们的>>=
定义决定,至少在使用lambda-no-do表示法时是这样吗?
我们可以在monad定义中使用这个lambda表达式并将一些东西传递给它,导致它再次打印屏幕吗?我们应该吗?
如果我们想在putStrLn
定义中使用>>=
,我们是否必须将一些任意值传递给k
才能获得putStrLn
功能?
谢谢。和平。
答案 0 :(得分:2)
我只会给你一个部分答案,因为我不明白,你究竟在问什么。对不起。
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
。
答案 1 :(得分:2)
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)))
这些括号完全是多余的,但它们突出了各种表达式的范围。请注意,a
和b
在调用putStrLn (a ++ b)
时仍在范围内。
答案 2 :(得分:1)
未标记的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!)
现在关于(&gt;&gt; =)的类型。 GHCI打印下一个(>>=) :: Monad m => m a -> (a -> m b) -> m b
。
因此k
只是Monad m
将使用的lambda,它所要做的只是>>=
的“实现接口”。
答案 3 :(得分:1)
do
是否等同于以下内容?getLine >>= \a -> getLine >>= \b -> putStrLn (a ++ b)
是的,这个。
如果第一个是正确的,那么不会导致错误,因为在monad定义中未定义错误吗?
没有。 ->
箭头语法是右关联的:
getLine >>= (\a -> getLine >>= (\b -> putStrLn (a ++ b)))
它形成一个封闭,其中a
仍然在范围内。