Haskell Parser排序不起作用

时间:2018-04-13 15:04:32

标签: haskell monads

我最近才learning Haskell。我在书中尝试了下面的代码。但它没有按预期工作。

p :: Parser (Char, Char)
    p = do x ← item
           item
           y ← item
           return (x , y)
这些书说:

  

>解析p" abcdef"
   [(('a','c')," def")]

但是当我尝试它时,它就像是:

*Main> pp "abcde"
[(([('a',"bcde")],[('a',"bcde")]),"abcde")]

排序运算符(+>> =)有效,但不是do表示法。为什么呢?

这是完整的代码。 GHCi 8.2.2

{-# OPTIONS_GHC -fno-warn-tabs #-}
type Parser a = String -> [(a, String)]

item :: Parser Char
item = \inp -> case inp of
        [] -> []
        (x:xs) -> [(x, xs)]


parse :: Parser a -> String -> [ (a, String) ]
parse p inp = p inp


return' :: a -> Parser a
return' v = \inp -> [(v,inp)]


(+>>=) :: Parser a -> (a -> Parser b) -> Parser b
p +>>= f = \inp -> case parse p inp of
                    [] -> []
                    [(v, out)] -> parse (f v) out


p :: Parser (Char, Char)
p = item +>>= \x -> 
    item +>>= \_ ->
    item +>>= \y ->
    return' (x,y)


pp = do  x <- item
         item
         y <- item
         return' (x,y)  --return (x,y) this won't work either

输出:

*Main> p "abcde"
[(('a','c'),"de")]
*Main> pp "abcde"
[(([('a',"bcde")],[('a',"bcde")]),"abcde")]

我哪里出错了?如何使用do notation编写函数p?

1 个答案:

答案 0 :(得分:3)

对于编译器,您的Parser类型看起来像这样(不是实际的Haskell语法):

type Parser a = ((->) String) α
 where α = [(a, String)]

因此,当您使用>>=(在do语法调用的引擎盖下)使用此类型的操作时,它会采用签名

(>>=) :: ______m______ α -> (α -> ______m______ β) -> ______m______ β
(>>=) :: ((->) String) α -> (α -> ((->) String) β) -> ((->) String) β

(>>=) :: Parser a -> ([(a,String)] -> Parser b) -> Parser b

(+>>=)的签名不同 - 你真正想要的只是

(>>=)  :: __m___ a -> (a -> __m___ b) -> __m___ b
(+>>=) :: Parser a -> (a -> Parser b) -> Parser b

IOW,>>=您实际上并没有使用Parser作为monad,而是Parser将您重定向到另一个monad(函数aka Reader monad)。

要实际让Parser表现为monad本身,使用+>>=的语义,必须使编译器将其识别为实际的不可分解实体。这意味着,您需要newtype而不是type

newtype Parser a = Parser (String -> [(a, String)])

item :: Parser Char
item = Parser $ \inp -> case inp of
        [] -> []
        (x:xs) -> [(x, xs)]


parse :: Parser a -> String -> [ (a, String) ]
parse (Parser p) inp = p inp


return' :: a -> Parser a
return' v = Parser $ \inp -> [(v,inp)]


(+>>=) :: Parser a -> (a -> Parser b) -> Parser b
Parser p +>>= f = Parser $ \inp -> case parse p inp of
                    [] -> []
                    [(v, out)] -> parse (f v) out


p :: Parser (Char, Char)
p = item +>>= \x -> 
    item +>>= \_ ->
    item +>>= \y ->
    return' (x,y)


pp = do  x <- item
         item
         y <- item
         return' (x,y)

此处,pp现在将提供明确的编译错误Could not deduce Monad Parser from a use of do notation...。这表明编译器实际上正在尝试做正确的事情,但为了实际工作,你仍然需要写出实例声明。这很容易,因为运营商已经在那里:

instance Functor Parser where
  fmap = liftM  -- makes use of the `Monad` instance below
instance Applicative Parser where
  pure = return'
  (<*>) = ap
instance Monad Parser where
  return = return'
  (>>=) = (+>>=)

虽然这是一个向后的定义 - 你正在使用最强大和最特殊的接口(monad)来定义仿函数类型的更基本的操作。首选的实例化路径是自上而下:

newtype Parser a = Parser (String -> [(a,String)]
  deriving (Functor)

instance Applicative Parser where
  pure v = Parser $ \inp -> [(v,inp)]
  Parser pf <*> Parser px = Parser $
     \inp -> case pf inp of
        [] -> []
        [(f,out)] -> f <$> px out

instance Monad Parser where
  return = pure
  Parser p >>= f = ...