是的,如果do
您的块中有不止一行,并且您甚至使用该do
符号。
完整的do
-notation 语法还包括显式分隔符——花括号和分号:
main = do { putStrLn "What's your name?"
; name <- getLine
; putStrLn ("Hello " ++ name)
}
有了它们,缩进除了编码风格之外没有任何作用(良好的缩进提高了可读性;显式分隔符确保了代码的健壮性,消除了与空白相关的脆弱性)。所以当你只有一行 IO 代码时,比如
main = do { print "Hello!" }
没有分号,没有需要注意的缩进,花括号和do
关键字本身变得多余:
main = print "Hello!"
所以,不,并非总是如此。但很多时候确实如此,而且代码的统一性对可读性大有帮助。
do
块转换为一元代码,但您可以首先将此事实视为实现细节。事实上,你应该。您可以在精神上将do
符号公理化地视为嵌入式语言。此外,无论如何,就是这样。
简化的do
- 语法是:
do { pattern1 <- action1
; pattern2 <- action2
.....................
; return (.....)
}
每个都是一些 monad和一些结果类型的 Haskell 类型值。每个都产生自己的结果类型,而所有s 必须属于相同的monad 类型。actioni
M ai
M
ai
action
ai
action
M
每个都从相应的操作接收先前“计算”的结果。patterni
通配符_
可以用来忽略它。如果是这种情况,则_ <-
可以完全省略该部分。
“Monad”是一个可怕且无信息的词,但从概念上讲,它实际上只不过是 EDSL。嵌入式领域特定语言意味着我们有原生的 Haskell 值代表(在这种情况下)I/O 计算。我们用这种语言编写我们的I/O程序,它成为一个原生 Haskell 值,我们可以像对任何其他原生 Haskell 值一样对其进行操作——将它们收集在列表中,将它们组合成更复杂的计算描述(程序) , ETC。
该main
值是我们的 Haskell 程序计算的一个这样的值。编译器看到它,并在运行时执行它所代表的I/O程序。
关键是我们现在可以拥有一个“函数” getCurrentTime
(从表面上看,在函数范式中是不可能的,因为它必须在单独的调用上返回不同的结果),因为它没有返回当前时间——它的动作当它描述的I/O程序由运行时系统运行时, describes将这样做。
在类型级别上,这些值反映的不仅仅是一些普通的 Haskell 类型a
,而是一个参数化类型,IO a
被“标记”IO
为属于这个特殊的I/O编程世界。
另请参阅:为什么 haskell 的 bind 函数将函数从 non-monadic 转换为 monadic。