鉴于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)
我已经完成了一些使用<$>
和<*>
制作解析器的示例。但我并不是完全不喜欢这些类型。
非常感谢任何理解它们的帮助。
答案 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
,此处a
为Char
。 parseDecimalPoint :: 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