'in'关键字有什么用?

时间:2012-07-26 17:39:14

标签: haskell syntax

在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)

这是一个很容易记住的规则,但我只是不明白它的原因。

3 个答案:

答案 0 :(得分:14)

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

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

    x <- getChar
    
  2. 忽略结果的计算,如

    putStrLn "hello"
    
  3. 一个let语句,如

    let x = 3 + 5
    
  4. 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类型的表达式,但有两种语法与语言中的任何其他语句都不对应:

  1. 将操作的结果与<-绑定:x <- action是一个“语句”,但不是表达式。此语法需要x :: aaction :: Monad m => m a
  2. in - 更少let的变体,类似于赋值语句(但对于右侧的纯代码)。在let x = expr中,必须是x :: aexpr :: a
  3. 的情况

    请注意,就像<-的使用可以被贬低(在这种情况下,进入>>=和lambda),in - 少let总是被贬低为常规let ... in ...

    do { let x = blah; ... }
        => let x = blah in do { ... }