2015-02-12 63 views
2

鉴于从教授Yorgey的UPenn class以下定义:写作解析器正JSON数/小数

newtype Parser a = Parser { runParser :: String -> Maybe (a, String) }

satisfy :: (Char -> Bool) -> Parser Char 
satisfy p = Parser f 
    where 
    f [] = Nothing -- fail on the empty input 
    f (x:xs)   -- check if x satisfies the predicate 
         -- if so, return x along with the remainder 
         -- of the input (that is, xs) 
     | p x  = Just (x, xs) 
     | otherwise = Nothing -- otherwise, fail 

而下面的代数数据类型:

type Key = String 

data Json = JObj Key JValue 
      | Arr [JValue] 
      deriving Show 

data JValue = N Double 
       | S String 
       | B Bool 
       | J Json 
       deriving Show 

我写的以下函数用于解析带小数点的位置JSON号码:

parseDecimalPoint :: Parser Char 
parseDecimalPoint = satisfy (== '.') 

type Whole = Integer 
type Decimal = Integer 

readWholeAndDecimal :: Whole -> Decimal -> Double 
readWholeAndDecimal w d = read $ (show w) ++ "." ++ (show d) 

parsePositiveDecimal:: Parser JValue 
parsePositiveDecimal = (\x _ y -> f x y) <$> ( 
     (oneOrMore (satisfy isNumber)) <*> parseDecimalPoint <*> 
      (zeroOrMore (satisfy isNumber))) 
    where 
    f x [] = N (read x) 
    f x y = N (-(readWholeAndDecimal (read x) (read y))) 

但是我得到了下面的编译时错误:

JsonParser.hs:30:25: 
    Couldn't match expected type ‘t0 -> [Char] -> JValue’ 
       with actual type ‘JValue’ 
    The lambda expression ‘\ x _ y -> f x y’ has three arguments, 
    but its type ‘String -> JValue’ has only one 
    In the first argument of ‘(<$>)’, namely ‘(\ x _ y -> f x y)’ 
    In the expression: 
     (\ x _ y -> f x y) 
     <$> 
     ((oneOrMore (satisfy isNumber)) <*> parseDecimalPoint 
     <*> (zeroOrMore (satisfy isNumber))) 

JsonParser.hs:30:49: 
    Couldn't match type ‘[Char]’ with ‘Char -> [Char] -> String’ 
    Expected type: Parser (Char -> [Char] -> String) 
     Actual type: Parser [Char] 
    In the first argument of ‘(<*>)’, namely 
     ‘(oneOrMore (satisfy isNumber))’ 
    In the first argument of ‘(<*>)’, namely 
     ‘(oneOrMore (satisfy isNumber)) <*> parseDecimalPoint’ 

在我parsePositiveDecimal功能,我的类型的理解是:

(String -> Char -> String -> JValue) <$> (Parser String <*> Parser Char <*> Parser String)

我已经通过工作几个例子使解析器与<$><*>。但是我并不完全喜欢这些类型。

任何帮助了解他们也将不胜感激。

+2

应当括号为'(\ X _Ÿ - > FXY)<$> (一次或更多(满足ISNUMBER))<*> parseDecimalPoint <*> (零次或多次(满足isNumber)'。 – Cactus 2015-02-12 04:30:38

回答

2

仙人掌是正确的。我会扩展一些类型。

<$> :: Functor f => (a -> b) -> f a -> f b

我们f这里是Parser,和第一个参数<$>的类型为String -> Char -> String -> JValue。请记住,这可以理解为一个函数,它需要String并返回一个函数Char -> String -> JValue因此,a类型变量填入String

由此我们可以看出,<$>的第二个参数需要是Parser StringoneOrMore (satisfy isNumber)具有该类型。

综上所述,我们现在有:

(\x _ y -> f x y) <$> (oneOrMore (satisfy isNumber)) :: Parser (Char -> String -> JValue)

我们从3个参数的函数不涉及Parser都走了,裹在Parser.申请的2个参数的函数此功能是一个参数,Char,我们需要:

(<*>) :: Applicative f => f (a -> b) -> f a -> f b

fParser再次,a这里是CharparseDecimalPoint :: Parser Char对于<*>的右侧具有所需的类型。

(\x _ y -> f x y) <$> (oneOrMore (satisfy isNumber)) <*> parseDecimalPoint :: Parser (String -> JValue)

我们这样做是一个更多的时间,来获得:知道运营商的优先级和结合,以消除一些括号

(\x _ y -> f x y) <$> oneOrMore (satisfy isNumber) <*> parseDecimalPoint <*> zeroOrMore (satisfy isNumber) :: Parser JValue

我冤大头。这就是我看到大多数这样的代码写的,但也许仙人掌的版本更清晰。甚至完全括号中的版本,强调关联性:

( ((\x _ y -> f x y) <$> (oneOrMore (satisfy isNumber))) <*> parseDecimalPoint) <*> (zeroOrMore (satisfy isNumber)) :: Parser JValue