Подтвердить что ты не робот

Что с ключевым словом "in"?

В Haskell, почему вы не используете "in" с "let" внутри блока do, но в противном случае вы должны?

Например, в несколько надуманных примерах ниже:

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)

Это достаточно простое правило помнить, но я просто не понимаю причину этого.

4b9b3361

Ответ 1

Вы предоставляете выражения для определения как afunc, так и amfunc. Let-выражения и do-blocks - оба выражения. Однако, хотя выражение let представляет новое связывание, которое охватывает все выражения, указанные после ключевого слова "in", блок do не состоит из выражений: это последовательность операторов. В do-блоке есть три формы операторов:

  • вычисление, результат которого связан с некоторой переменной x, как в

    x <- getChar
    
  • вычисление, результат которого игнорируется, как в

    putStrLn "hello"
    
  • Утверждение let, как в

    let x = 3 + 5
    

Оператор let представляет новое связывание, как это делают let-выражения. Объем этой новой привязки распространяется на все остальные операторы в блоке do.

Короче говоря, то, что происходит после "in" в let-expression, является выражением, тогда как то, что происходит после выражения 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)

отлично работает.

Ответ 2

Вы действительно можете использовать let .. in в do-notation. Фактически, согласно отчету Haskell, следующий

do {let decls; stmts}

desugars в

let decls in do {stmts}

Я полагаю, что это полезно, потому что в противном случае вы могли бы иметь глубокий отступ или разграничение "in" -block, начиная с вашего... до самого конца блока do.

Ответ 3

Короткий ответ: блоки Haskell do смешны. Haskell - это язык, основанный на выражениях, за исключением блоков do, потому что точкой блоков do является предоставление синтаксиса "statement". Большинство "операторов" - это просто выражения типа Monad m => m a, но есть два синтаксиса, которые не соответствуют чему-либо еще на языке:

  • Связывание результата действия с помощью <-: x <- action является "оператором", но не выражением. Этот синтаксис требует x :: a и action :: Monad m => m a.
  • in -less вариант let, который похож на оператор присваивания (но для чистого кода с правой стороны). В let x = expr это должно быть так, что x :: a и expr :: a.

Обратите внимание, что как можно использовать дескрипторы <- (в этом случае, в >>= и лямбда), in -less let всегда можно удалить в регулярный let ... in ...:

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