Haskell monad流程理解

时间:2017-10-08 17:22:53

标签: haskell lambda functional-programming monads

我将在底部以示例重述我的问题。

当我说mk时,我指的是函数调用中的左右值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

ab绑定到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功能?

谢谢。和平。

4 个答案:

答案 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)))

这些括号完全是多余的,但它们突出了各种表达式的范围。请注意,ab在调用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仍然在范围内。