用正/小数写正解JSON数的解析器

时间:2015-02-12 03:21:09

标签: parsing haskell

鉴于Yorgey教授UPenn class的以下定义:

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)

我已经完成了一些使用<$><*>制作解析器的示例。但我并不是完全不喜欢这些类型。

非常感谢任何理解它们的帮助。

1 个答案:

答案 0 :(得分:2)

仙人掌是正确的。我将对类型进行一些扩展。

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

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

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

总之,我们现在有:

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

我们已经从3个参数的函数中删除了Parser根本不涉及Parser.中包含的2个参数的函数。将此函数应用于它&# 39;下一个论点Char,我们需要:

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

f再次Parser,此处aCharparseDecimalPoint :: 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

我已经利用了解运算符的优先级和关联性来删除一些括号。这就是我看到大多数这样的代码编写的方式,但也许Cactus的版本更清晰。甚至是完全括号的版本,强调关联性:

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