我正在完成Brent Yorgey Haskell课程的学习,而在定义Applicative的良好实例时遇到了麻烦。解析器的定义如下:
newtype Parser a = Parser { runParser :: String -> Maybe (a, String) }
该函数获取一个字符串,解析一定数量的输入,然后返回Maybe元组,其中第一个值是解析器的类型,其余为未解析的字符串余数。例如,这是一个用于正整数的解析器:
posInt :: Parser Integer
posInt = Parser f
where
f xs
| null ns = Nothing
| otherwise = Just (read ns, rest)
where (ns, rest) = span isDigit xs
任务是为解析器创建一个Applicative实例。我们从一个Functor实例开始(我认为这是相对简单的):
first :: (a -> b) -> (a,c) -> (b,c)
first f (a, c) = (f a, c)
instance Functor Parser where
fmap f p = Parser f'
where f' s = fmap (first f) $ (runParser p) s
然后我尝试了Applicative:
collapse (Just (Just a)) = Just a
collapse _ = Nothing
extract (Just a, Just b) = Just (a,b)
extract _ = Nothing
appliedFunc :: Parser (a->b) -> Parser a -> String -> Maybe (b, String)
appliedFunc p1 p2 str = extract (f <*> fmap fst result2, fmap snd result2)
where result1 = (runParser p1) str
f = fmap fst result1
result2 = collapse $ fmap (runParser p2) $ fmap snd result1
instance Applicative Parser where
pure a = Parser (\s -> Just (a, s))
p1 <*> p2 = Parser (appliedFunc p1 p2)
y 所以我的问题是,如何使我的Applicative实例更整洁,更不易阅读?我觉得这个问题有一个简单的答案,但是我还无法确定这些类型。
我认为您还没有Monad
课程。您正在使用的方式collapse
,并fmap
指示我,你基本上是重塑Monad
s到解决这个问题,特别是Monad Maybe
实例。实际上,您collapse
与join
此monad相同。的确使用是解决这个问题的一个非常优雅的方式,但也许有点“欺骗”在这一点上。以下是在使用您的函数时可以得到的最佳形状:
appliedFunc p1 p2 str = collapse $ fmap step1 (runParser p1 str)
where
step1 (f, str2) = collapse $ fmap step2 (runParser p2 str2)
where
step2 (x, str3) = Just (f x, str3)
一旦设置Monad
正确,就应该可以使用更简洁的(>>=)
运算符和/或do
符号来重写它。
几乎一样简单但又不需要重新构造monad的另一种替代方法是使用Maybe
s的显式模式匹配。然后您会得到类似以下内容的信息:
appliedFunc p1 p2 str = case runParser p1 str of
Nothing -> Nothing
Just (f, str2) -> case runParser p2 str2 of
Nothing -> Nothing
Just (x, str3) -> Just (f x, str3)
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句