我有一个monadic解析器,我正在实现它作为练习。它的签名如下:
type Parser err src target = ExceptT err (State [src]) target
我已经实现了许多基本帮助程序,但是我遇到了一个需要负向前瞻的用例。特别是,我想我想做一些这样的签名:
notFollowedBy :: e -> Parser e s t -> Parser e s t' -> Parser e s t
notFollowedBy followedByError parser shouldFail = -- ...
我的想法是它可以在这样的上下文中使用:
foo = letter `notFollowedBy'` digit
where notFollowedBy' = notFollowedBy FollwedByDigitError
我很难实施notFollowedBy
,但出于各种原因:
shouldFail
的方法,以便我可以反转它的ExceptT结果(即如果它抛出我想要捕获它并且什么也不做,但如果它没有抛出我需要抛出{{ 1}})
notFollowedByError
不会在这里做(我不认为),我无法找到一种方法来使用catchError
从runExceptT
获取Either e t'
shouldFail
之前,我需要从shouldFail
保存状态,因为在运行StateT
之后我需要恢复状态(好像这个解析器没有运行)。但是我正在使用Lazy shouldFail
,因此我不清楚是否需要将所有内容切换到严格的内容以便允许这种情况我最好的刺不会编译,但它看起来像这样:
StateT
(作为实现说明,第一个参数实际上是notFollowedBy :: (t' -> e) -> Parser e s t -> Parser e s t' -> Parser e s t
notFollowedBy onUnexpected parser shouldFail = do
parsed <- parser
state <- get -- This isn't strict
result <- runExceptT shouldFail -- This doesn't typecheck
case result of
Left err -> put state >> return parsed
Right t -> put state >> throwError $ onUnexpected t
,因为我想允许根据第二个解析器返回的信息自定义抛出的错误。但我不认为这对我的问题很重要。 )
类型检查失败的原因是它期待(t' -> e)
,但获得ExceptT (ExceptT e (State [s])) t
(Parser e s t
)。
在倾听文档并阅读ExceptT和StateT的一些来源之后,我最好的猜测是我需要模仿ExceptT e (State [s]) t
(在catchError
上匹配)然后使用{{1 }}。这是我对此的草率(也无法编译):
Either
第二次,类型检查员似乎对许多事情感到不满。特别是,liftCatch
(来自notFollowedBy :: (t' -> e) -> Parser e s t -> Parser e s t' -> Parser e s t
notFollowedBy onUnexpected parser shouldFail = do
state <- get
result <- parser
catchSuccess' result shouldFail (throwError . onUnexpected)
put state
return result
where catchSuccess' result = liftCatch (catchSuccess result)
catchSuccess r (Left l) _ = Right r
catchSuccess _ (Right r) h = Left (h r)
)似乎不是我们想要的(因为它希望liftCatch
返回State.Lazy
)。
此时我只是在试图安抚编译器时随意排列。任何人都可以提供有关如何实施catchSuccess'
的任何建议吗?
编辑:在咨询了我如何实施ExceptT
(下面)后,似乎状态排序不是问题(尽管这对我来说是个谜)。因此,我的主要问题是创建notFollowedBy
(optional
)的反向。
catchError
我正在尝试编写一个带有此签名的函数,当catchSuccessAndSuppressError
没有抛出异常时(在运行option :: Parser e s t -> Parser e s t -> Parser e s t
option parserA parserB = do
state <- get
parserA `catchError` \_ -> put state >> parserB
后运行时)抛出followedByError
。返回时的状态应与shouldFail
运行后的状态相同。
parser