haskell 中的等号 (=) 与左箭头 (<-) 符号

Equal (=) Vs left arrow (<-) symbols in haskell

工作代码:

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: parse error on input '='

那么,<-= 有何不同?

do
    x <- y
    f x

相当于:

y >>= \x -> f x

do
    let x = y
    f x

相当于

f y

let/= 没有单子绑定,而 <- 有。

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

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

  • x <- action 运行s IO action, 获取其结果,并将其绑定到 x
  • let x = action 定义 x 等同于 action,但没有 运行 任何东西。稍后,您可以使用 y <- x 表示 y <- 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,你需要检查 >>= 实际上是什么,并考虑 do.

的脱糖

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

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

所以writeFile想要一个FilePath和一个String。你想从 readFile 得到 String - 但是 readFile returns IO String 而不是 String.

Haskell是一门很有原则的语言。它在 pure 函数(每次使用相同的参数调用时给出相同的输出)和 impure 代码(可能给出不同的结果,例如,如果函数取决于某些用户输入)。处理 input/output (IO) 的函数总是有一个标有 IO 的 return 类型。类型系统确保你不能在纯函数中使用不纯的 IO 代码——例如,不是 returning a String 函数 readFile returns an IO String.

这是 <- 符号的重要之处。它允许您获取 IO 内的 String 并确保无论您对该字符串做什么,您正在定义的函数将始终标记为 IO。比较以下-

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

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

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

这就是我们想要的。如果你有一个 return 是 IO a 的函数并且你想访问 a ,你需要使用 <- 将结果分配给一个名称。如果您的函数没有 return IO a ,或者如果您不想在 IO 内获取 a 那么您可以使用 = .

let x = readFile file1

这执行操作“readFile file1”并将操作存储在x

x <- readFile file1

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

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