Что с ключевым словом "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)
Это достаточно простое правило помнить, но я просто не понимаю причину этого.
Ответы
Ответ 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 { ... }