'in'关键字有什么用?

bro*_*s94 15 syntax haskell

在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

您提供表达式来定义afuncamfunc.let-expression和do-blocks都是表达式.但是,虽然let-expression引入了一个新的绑定,该绑定的范围围绕'in'关键字后面给出的表达式,但是do-block不是由表达式组成的:它是一系列语句.do-block中有三种形式的语句:

  1. 计算结果绑定到某个变量x,如

    x <- getChar
    
    Run Code Online (Sandbox Code Playgroud)
  2. 忽略结果的计算,如

    putStrLn "hello"
    
    Run Code Online (Sandbox Code Playgroud)
  3. 一个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)

上面的代码导致G​​HC中出现以下错误消息:

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)

工作良好.


Sar*_*rah 9

你确实可以使用let .. indo-notation.实际上,根据Haskell报告,以下内容

做{let decls; 撑条}

desugars into

让我们去做{stmts}

我想这很有用,因为你可能需要有一些深度缩进或分隔"in"-block,从你的in ..转到do-block的最后.


Lui*_*las 6

简短的回答是Haskell do块很有趣.Haskell是一种基于表达式的语言 - 除了do块之外,因为块的要点do是提供各种"语句"语法.大多数"语句"只是类型的表达式Monad m => m a,但有两种语法与语言中的任何其他语句不对应:

  1. 将操作的结果与<-:绑定x <- action是"语句",但不是表达式.此语法需要x :: aaction :: Monad m => m a.
  2. 所述in的变体稀少let,这是像一个赋值语句(但在右手侧纯代码).在let x = expr,必须是x :: aexpr :: a.

需要注意的是,就像用途<-可脱(在这种情况下,进入>>=和λ),该in稀少let总是可以脱到正规let ... in ...:

do { let x = blah; ... }
    => let x = blah in do { ... }
Run Code Online (Sandbox Code Playgroud)