在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)
Run Code Online (Sandbox Code Playgroud)
这是一个很容易记住的规则,但我只是不明白它的原因.
mac*_*ron 15
您提供表达式来定义afunc
和amfunc
.let-expression和do-blocks都是表达式.但是,虽然let-expression引入了一个新的绑定,该绑定的范围围绕'in'关键字后面给出的表达式,但是do-block不是由表达式组成的:它是一系列语句.do-block中有三种形式的语句:
计算结果绑定到某个变量x
,如
x <- getChar
Run Code Online (Sandbox Code Playgroud)忽略结果的计算,如
putStrLn "hello"
Run Code Online (Sandbox Code Playgroud)一个let-statement,如同
let x = 3 + 5
Run Code Online (Sandbox Code Playgroud)let语句引入了一个新的绑定,就像let-expression一样.此新绑定的范围扩展到do-block中的所有剩余语句.
简而言之,let-expression中'in'之后的内容是一个表达式,而let表达式之后出现的是一系列语句.当然,我可以表达使用松懈表达特定语句的计算,但随后的结合的范围不会超出这句话延伸到后面的语句.考虑:
do putStrLn "hello"
let x = 3 + 5 in putStrLn "eight"
putStrLn (show x)
Run Code Online (Sandbox Code Playgroud)
上面的代码导致GHC中出现以下错误消息:
Not in scope: `x'
Run Code Online (Sandbox Code Playgroud)
而
do putStrLn "hello"
let x = 3 + 5
putStrLn "eight"
putStrLn (show x)
Run Code Online (Sandbox Code Playgroud)
工作良好.
你确实可以使用let .. in
do-notation.实际上,根据Haskell报告,以下内容
做{let decls; 撑条}
desugars into
让我们去做{stmts}
我想这很有用,因为你可能需要有一些深度缩进或分隔"in"-block,从你的in ..转到do-block的最后.
简短的回答是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
.需要注意的是,就像用途<-
可脱(在这种情况下,进入>>=
和λ),该in
稀少let
总是可以脱到正规let ... in ...
:
do { let x = blah; ... }
=> let x = blah in do { ... }
Run Code Online (Sandbox Code Playgroud)