【问题标题】:Equal (=) Vs left arrow (<-) symbols in haskellHaskell中的等号(=)与左箭头(<-)符号
【发布时间】:2015-02-20 08:19:17
【问题描述】:

工作代码:

import System
main = do
     [file1, file2] <- getArgs
     --copy file contents
     str <- readFile file1
     writeFile file2 str

崩溃代码:

import System
main = do
       [file1, file2] = getArgs
       str = readFile file1
       writeFile file2 str

当我尝试时,它抛出了一个错误:

a.hs:6:18:输入“=”时出现解析错误

那么,&lt;-= 有何不同?

【问题讨论】:

  • 您需要了解 Monad de-sugaring 才能真正了解它的适用性。
  • @Sibi 这在技术上是正确的,但我觉得应该可以在不提及 M 字的情况下回答这个问题。毕竟m***ds只是纯语言处理IO的一种方式。即使 Haskell 没有在语言中内置 m****ds,它仍然会区分纯代码和不纯代码,并且这个问题(或类似问题)仍然存在。
  • @ChrisTaylor 虽然可以在不解释 Monad 的情况下回答这个问题,但我真的怀疑它是否会帮助 OP 掌握这个概念。话虽如此,这里有很多很好的答案。

标签: haskell


【解决方案1】:

要了解真正的区别,您必须了解单子,以及@rightfold 在他们的回答中描述的脱糖。

对于 IO monad 的特定情况,如您的 getArgs 示例中,可以做出如下粗略但有用的直觉:

  • x &lt;- action 运行 IO action,获取其结果,并将其绑定到 x
  • let x = actionx 定义为等同于action,但不运行任何东西。稍后,您可以使用y &lt;- x 表示y &lt;- action

来自允许闭包的命令式语言的程序员可能会与 JavaScript 进行粗略的平行比较:

var action = function() { print(3); return 5; }

// roughly equivalent to x <- action
print('test 1')
var x = action()  // output:3
// x is 5

// roughly equivalent to let y = action
print('test 2')
var y = action    // output: nothing
// y is a function

// roughly equivalent to z <- y
print('test 3')
var z = y()       // output:3
// z is 5

再次强调:此比较仅关注 IO。对于其他 monad,您需要检查 &gt;&gt;= 究竟是什么,并考虑对 do 进行脱糖。

【讨论】:

    【解决方案2】:
    do
        x <- y
        f x
    

    相当于:

    y >>= \x -> f x
    

    do
        let x = y
        f x
    

    等价于

    f y
    

    let/= 没有单子绑定,而&lt;- 有。

    【讨论】:

    • let x = y in f x(实际上 is 相当于第二个 do 表达式)是“相当于”f y 是危险的,主要是因为它可能不明显读者“等效”是什么意思;这两个表达式的值相同,但它们的语义可能不同。
    • @user2407038 不,它们完全一样。没有任何区别。
    • 这让我很难过(简化版)testF box = do a &lt;- box; let b = do x &lt;- Just "from let"; return x; return (fmap (++ box) b) 让我们走私一个 monad 而没有任何额外的回报,b 被放在了silently 的框中。同时我尝试使用... b &lt;- do x &lt;- Just "from let"; return x ..." 编译,但我没有找到给 testF 运行它的内容:-)
    【解决方案3】:

    代码无法编译,因为类型不匹配。让我们加载一个 GHCI 会话并查看您正在使用的函数的类型 -

    > :t writeFile
    writeFile :: FilePath -> String -> IO ()
    >
    > :t readFile
    readFile :: FilePath -> IO String
    

    所以writeFile 想要一个FilePath 和一个String。您想从readFile 获取String - 但readFile 返回IO String 而不是String

    Haskell 是一种非常有原则的语言。它在 pure 函数(每次使用相同的参数调用它们时给出相同的输出)和 impure 代码(可能给出不同的结果,例如,如果功能取决于一些用户输入)。处理输入/输出 (IO) 的函数总是有一个用IO 标记的返回类型。类型系统确保您不能在纯函数中使用不纯的 IO 代码 - 例如,函数 readFile 不会返回 String,而是返回 IO String

    这就是&lt;- 符号很重要的地方。它允许您访问IO 中的String,并确保无论您对该字符串做什么,您定义的函数都将始终标记为IO。比较以下 -

    > let x = readFile "tmp.txt"
    > :t x
    x :: IO String
    

    这不是我们想要的,对这个

    > y <- readFile "tmp.txt"
    > :t y
    y :: String
    

    这就是我们想要的。如果您有一个返回 IO a 的函数并且您想访问 a ,则需要使用 &lt;- 将结果分配给名称。如果您的函数没有返回 IO a ,或者如果您不想在 IO 中访问 a ,那么您可以使用 =

    【讨论】:

    • OP 的代码有一个语法错误。似乎应该在隐藏在它后面的类型错误之前解决这个问题。
    • 可以强调的是,必须通过&lt;- 才能获得计算结果强制一个人在内部 IO @ 中执行此操作987654349@ 块,能够做到这一点。从而强制将不纯 IO 与纯代码分离。
    【解决方案4】:
    let x = readFile file1
    

    这将采取行动“readFile file1”并将该行动存储在x中。

    x <- readFile file1
    

    执行动作“readFile file1”并将动作的结果存储在x中。

    在第一个示例中,x 是一个未执行的 I/O 操作对象。在第二个示例中,x 是磁盘上文件的内容。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-07
      • 1970-01-01
      • 1970-01-01
      • 2014-03-03
      • 1970-01-01
      相关资源
      最近更新 更多