Bind vs Assignment в Хаскеле
Я работаю над учебным пособием " Напиши себе схему", и один блок кода заставил меня задуматься о разнице между связыванием и присваиванием:
parseAtom = do first <- letter <|> symbol
rest <- many (letter <|> digit <|> symbol)
let atom = first:rest
return $ case atom of
"#t" -> Bool True
"#f" -> Bool False
_ -> Atom atom
Зачем let atom =
вместо atom <-
? Таким образом, я попытался:
parseAtom = do first <- letter <|> symbol
rest <- many (letter <|> digit <|> symbol)
atom <- first : rest
return $ case atom of
"#t" -> Bool True
"#f" -> Bool False
_ -> Atom atom
И я получаю ошибку компиляции:
Couldn't match expected type `[Char]'
against inferred type `Text.Parsec.Prim.ParsecT
String () Data.Functor.Identity.Identity Char'
In a stmt of a 'do' expression: atom <- first : rest
Я не могу понять, что именно это означает, что, вероятно, из-за неточного понимания do
или монады. (Я прочитал Learn You a Haskell вместе с различными другими учебными пособиями по монаде /do, и другие вопросы SO отмечают, что отступы часто вызывают проблемы здесь, но я думаю, что я делаю отступ правильно)
2 ответа
Вы внутри монады парсера, поэтому правая сторона <-
должно быть выражением парсера. тем не мение first : rest
это просто список (в частности, строка), а не выражение синтаксического анализатора.
Какие v <- someParser
делает это применяет данный анализатор к входу, а затем сохраняет сопоставленный текст в v
, Строка не является синтаксическим анализатором и не может быть применена к вводу, и не будет соответствующего текста для хранения в v
, Так что все, что вы можете сделать, это сохранить строку в v
что бы вы сделали, написав let v = someString
,
Вы имеете дело с двумя синтаксическими сахарными конструкциями: do-нотация и блок let внутри do-нотации. Все должно стать понятным, если мы просто расскажем о вашей правильной реализации функции.
оригинал
parseAtom = do
first <- letter <|> symbol
rest <- many (letter <|> digit <|> symbol)
let atom = first:rest
return $ case atom of
"#t" -> Bool True
"#f" -> Bool False
_ -> Atom atom
Дай блок дезугаред
parseAtom = do
first <- letter <|> symbol
rest <- many (letter <|> digit <|> symbol)
let
atom = first : rest
in do
return $ case atom of
"#t" -> Bool True
"#f" -> Bool False
_ -> Atom atom
Десугаред полностью
parseAtom =
(letter <|> symbol) >>= \first ->
many (letter <|> digit <|> symbol) >>= \rest ->
let
atom = first : rest
in return $ case atom of
"#t" -> Bool True
"#f" -> Bool False
_ -> Atom atom
Следует также отметить, что простое выражение let внутри нотации do можно заменить на выражение bind, как вы и ожидали - все, что вам нужно сделать, это просто вставить return
, Так let atom = first:rest
можно перевести на atom <- return $ first : rest
,