Haskell 解析器排序不起作用

标签 haskell monads

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

p :: Parser (Char, Char)
    p = do x ← item
           item
           y ← item
           return (x , y)

书上说:

>parse 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 表示法编写函数 p?

最佳答案

对于编译器来说,您的 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 (该函数又名 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 derduc Monad Parser from a use of do notation...。这表明编译器实际上正在尝试做正确的事情,但要使其真正起作用,您仍然需要写出实例声明。这很容易,因为运算符(operator)已经在那里:

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'
  (>>=) = (+>>=)

不过,这有点向后定义 - 您正在使用最强大和最特殊的接口(interface)(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 = ...

关于Haskell 解析器排序不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49820033/

相关文章:

haskell - 为什么 Haskell 缺少文字 Data.Map 构造函数语法?

haskell - 使函数成为向量类型类的实例

haskell - 具有 "real"返回函数的 monad

haskell - `<$>`/`<*>`/`join` 的组合等于 `>>=` 吗?

scala - 如果值不为空,则添加到列表

haskell - 需要帮助理解 Haskell 中的 (\x -> )

haskell - 从 Haskell 中的数据中获取值

haskell - 在 c2hs 中将一个裸结构从 C 返回到 Haskell

scala - 接受两个monadic值并返回一个monadic值的泛型函数

haskell - 为什么我无法获得增加的计数器值?