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,

Другие вопросы по тегам