2015-02-12 64 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