我最近才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?
答案 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 = ...