首页 文章

Haskell monad流程理解

提问于
浏览
2

我将用底部的例子重述我的问题 .

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

ab 绑定到从运行 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 回答

  • 1

    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 是'contained element' . 在这个lambda表达式中,调用了一个新表达式 . 那个表达是

    getLine >>= \b -> putStrLn (a ++ b)
    

    此表达式仍然是'inside'第一个lambda表达式,这意味着 a 仍在范围内 .

    如果它有帮助,你可以在表达式周围添加更多括号:

    getLine >>= (\a -> getLine >>= (\b -> putStrLn (a ++ b)))
    

    这些括号完全是多余的,但它们突出了各种表达式的范围 . 请注意,调用 putStrLn (a ++ b) 时, ab 仍在范围内 .

  • 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 替换 . 所以不用再担心了.1122733_ .

  • 1

    没有用 do block 就像 getLine >>= \a -> getLine >>= \b -> putStrLn (a ++ b) 你不是't need to pass all parameters from previous call because of local scopes, let' s表示它们:

    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,所有它必须做的是"implement interface"为 >>= .

  • 2

    这是否等同于以下? getLine >> = \ a - > getLine >> = \ b - > putStrLn(a b)

    是的,这个 .

    如果第一个是正确的,那么不会导致错误,因为在monad定义中未定义?

    不, -> 箭头语法是右关联:

    getLine >>= (\a -> getLine >>= (\b -> putStrLn (a ++ b)))
    

    它形成一个闭包,其中 a 仍然在范围内 .

相关问题