在Haskell中,为什么不在do-block中使用'in'和'let',否则你必须这样做?
例如,在下面有些人为的例子中:
afunc :: Int -> Int
afunc a =
let x = 9 in
a * x
amfunc :: IO Int -> IO Int
amfunc a = do
let x = 9
a' <- a
return (a' * x)
这是一个很容易记住的规则,但我只是不明白它的原因。
答案 0 :(得分:14)
您提供的表达式用于定义afunc
和amfunc
。 let-expression和do-blocks都是表达式。但是,虽然let-expression引入了一个新的绑定,它围绕'in'关键字后面给出的表达式,但是do-block不是由表达式组成的:它是一系列语句。 do-block中有三种形式的语句:
计算结果绑定到某个变量x
,如
x <- getChar
忽略结果的计算,如
putStrLn "hello"
一个let语句,如
let x = 3 + 5
let语句引入了一个新的绑定,就像let-expressions一样。这个新绑定的范围扩展到了do-block中的所有剩余语句。
简而言之,let-expression中'in'之后的内容是一个表达式,而let表达式之后的内容是一系列语句。我当然可以使用let-expression来表达特定语句的计算,但是然后绑定的范围不会扩展到该语句之外的语句。考虑:
do putStrLn "hello"
let x = 3 + 5 in putStrLn "eight"
putStrLn (show x)
以上代码在GHC中导致以下错误消息:
Not in scope: `x'
,而
do putStrLn "hello"
let x = 3 + 5
putStrLn "eight"
putStrLn (show x)
工作正常。
答案 1 :(得分:9)
您确实可以在会话中使用let .. in
。事实上,根据Haskell报告,以下
做{let decls;撑条}
desugars into
让我们去做{stmts}
我认为它很有用,因为你可能需要有一些深度缩进或分隔“in”-block,从你的in ..转到do-block的最后。
答案 2 :(得分:5)
简短的回答是Haskell do
块很有趣。 Haskell是一种基于表达式的语言 - 除了do
块之外,因为do
块的要点是提供各种“语句”语法。大多数“语句”只是Monad m => m a
类型的表达式,但有两种语法与语言中的任何其他语句都不对应:
<-
绑定:x <- action
是一个“语句”,但不是表达式。此语法需要x :: a
和action :: Monad m => m a
。in
- 更少let
的变体,类似于赋值语句(但对于右侧的纯代码)。在let x = expr
中,必须是x :: a
和expr :: a
。请注意,就像<-
的使用可以被贬低(在这种情况下,进入>>=
和lambda),in
- 少let
总是被贬低为常规let ... in ...
:
do { let x = blah; ... }
=> let x = blah in do { ... }