工作代码:
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:对输入'='
进行解析错误
那么<-
与=
有何不同?
答案 0 :(得分:81)
要了解真正的区别,你必须了解monad,以及@rightfold在回答中所描述的堕落。
对于IO monad的特定情况,如在getArgs
示例中,粗略但有用的直觉可以如下:
x <- action
运行 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
的消亡。
答案 1 :(得分:44)
do
x <- y
f x
相当于:
y >>= \x -> f x
do
let x = y
f x
相当于
f y
即。 let
/ =
没有monadic绑定而<-
没有。
答案 2 :(得分:14)
代码无法编译,因为类型不匹配。让我们加载GHCI会话并查看您正在使用的功能类型 -
> :t writeFile
writeFile :: FilePath -> String -> IO ()
>
> :t readFile
readFile :: FilePath -> IO String
因此writeFile
需要FilePath
和String
。您希望从String
获取readFile
- 但readFile
返回IO String
而不是String
。
Haskell是一种非常有原则的语言。它区分纯函数(每次使用相同的参数调用时都提供相同的输出)和不纯的代码(可能会产生不同的结果,例如,如果功能取决于一些用户输入)。处理输入/输出(IO)的函数始终具有标记为IO
的返回类型。类型系统确保您不能在纯函数中使用不纯的IO
代码 - 例如,不是返回String
函数readFile
,而是返回IO String
。
这是<-
符号很重要的地方。它允许您到达String
内的IO
,并确保无论您使用该字符串做什么,您定义的函数将始终标有IO
。比较以下内容 -
> let x = readFile "tmp.txt"
> :t x
x :: IO String
这不是我们想要的,对此
> y <- readFile "tmp.txt"
> :t y
y :: String
这就是我们想要的。如果您有一个返回IO a
并且想要访问a
的函数,则需要使用<-
将结果分配给名称。如果您的功能没有返回IO a
,或者您不希望进入a
内的IO
,那么您可以使用=
答案 3 :(得分:12)
let x = readFile file1
这会采取行动&#34; readFile file1
&#34;并在x
中存储动作。
x <- readFile file1
此执行动作&#34; readFile file1
&#34;并在x
中存储操作的结果。
在第一个示例中,x
是未执行的I / O操作对象。在第二个示例中,x
是磁盘上文件的内容。