为什么 ParsecT 没有 MonadWriter 实例?
Why no MonadWriter instance for ParsecT?
我今天早些时候写了一些 Haskell。想出了一些类似
的东西
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
newtype Foo a = Foo { ParsecT String () (Writer DefinitelyAMonoid) a }
deriving (Functor, Applicative, Monad, MonadWriter DefinitelyAMonoid)
这没有编译。 “数据类型声明的 'deriving' 子句没有 (MonadWriter DefinitelyAMonoid (ParsecT String () (Writer DefinitelyAMonoid)))
的实例”,GHC 告诉我。所以我决定看看其他一些 MTL 类 是否可行,他们确实可行。 Reader
和 MonadReader
,以及 Writer
和 MonadWriter
。所以我 转向 被一个 Discord 用户引导到 Hackage,问题很明显:
MonadState s m => MonadState s (ParsecT s' u m)
MonadReader r m => MonadReader r (ParsecT s u m)
MonadError e m => MonadError e (ParsecT s u m)
没有 MonadWriter
实例可以通过 ParsecT
变压器!对我来说,这似乎只是一个疏忽,在 Github 上打开一个问题之前,我几乎没有意识到这可能是故意的。所以我看了一下 Megaparsec:
(Stream s, MonadState st m) => MonadState st (ParsecT e s m)
(Stream s, MonadReader r m) => MonadReader r (ParsecT e s m)
(Stream s, MonadError e' m) => MonadError e' (ParsecT e s m)
同样,没有 MonadWriter
。
为什么 Parsec 和 Megaparsec 都不为 ParsecT
提供 MonadWriter
实例?像这样的解析器 monad 是否有什么特别之处使它们无法与编写者相处融洽?是否有类似 this 的情况发生?
Parsec 是一个回溯单子。虽然 MonadWriter
您提出的实例并非不可能,但将 Writer
放在回溯单子下面是一件奇怪的事情。这是因为 monad 堆栈的内层提供了外层构建的“基础”效果——也就是说,无论 ParsecT u (Writer w)
做什么,它都必须只 就 tell
。因此,如果它 tell
是一个分支中的值,后来被回溯出去,则不可能“解开”该值,因此 Writer
的结果将更像是解析 算法 而不是 Parsec 呈现的数据流抽象的踪迹。它不是没用,但它很奇怪,如果 WriterT
在外面而 Parsec
在里面,你会有更自然的语义。
同样的论点适用于 State
。 Parsec 公开了它自己的用户状态功能(u
参数)和一个继承底层 monad 状态功能的 MonadState
实例。后者带有与 MonadWriter
相同的注意事项——状态遵循算法的轨迹,而不是数据流。我不知道为什么包含此实例而 Writer
实例不包含;他们都以同样的方式棘手。
-- I'm presuming the user might want a separate, non-backtracking
-- state aside from the Parsec user state.
instance (MonadState s m) => MonadState s (ParsecT s' u m) where
get = lift get
put = lift . put
Parsec 的用户状态,另一方面,遵循数据流,而不是计算,它与将 StateT
放在外部具有相同的效果。 Parsec 提供自己的状态工具而不是仅仅要求我们使用转换器,这似乎总是很奇怪——但是,现在想想,我怀疑这只是为了避免在你需要的时候把 lift
到处都是碰巧使用状态。
总而言之,他们可以提供您要求的 MonadWriter
实例,但我认为有充分的理由不提供 - 以阻止犯我认为您可能正在犯的错误。
我今天早些时候写了一些 Haskell。想出了一些类似
的东西{-# LANGUAGE GeneralizedNewtypeDeriving #-}
newtype Foo a = Foo { ParsecT String () (Writer DefinitelyAMonoid) a }
deriving (Functor, Applicative, Monad, MonadWriter DefinitelyAMonoid)
这没有编译。 “数据类型声明的 'deriving' 子句没有 (MonadWriter DefinitelyAMonoid (ParsecT String () (Writer DefinitelyAMonoid)))
的实例”,GHC 告诉我。所以我决定看看其他一些 MTL 类 是否可行,他们确实可行。 Reader
和 MonadReader
,以及 Writer
和 MonadWriter
。所以我 转向 被一个 Discord 用户引导到 Hackage,问题很明显:
MonadState s m => MonadState s (ParsecT s' u m)
MonadReader r m => MonadReader r (ParsecT s u m)
MonadError e m => MonadError e (ParsecT s u m)
没有 MonadWriter
实例可以通过 ParsecT
变压器!对我来说,这似乎只是一个疏忽,在 Github 上打开一个问题之前,我几乎没有意识到这可能是故意的。所以我看了一下 Megaparsec:
(Stream s, MonadState st m) => MonadState st (ParsecT e s m)
(Stream s, MonadReader r m) => MonadReader r (ParsecT e s m)
(Stream s, MonadError e' m) => MonadError e' (ParsecT e s m)
同样,没有 MonadWriter
。
为什么 Parsec 和 Megaparsec 都不为 ParsecT
提供 MonadWriter
实例?像这样的解析器 monad 是否有什么特别之处使它们无法与编写者相处融洽?是否有类似 this 的情况发生?
Parsec 是一个回溯单子。虽然 MonadWriter
您提出的实例并非不可能,但将 Writer
放在回溯单子下面是一件奇怪的事情。这是因为 monad 堆栈的内层提供了外层构建的“基础”效果——也就是说,无论 ParsecT u (Writer w)
做什么,它都必须只 就 tell
。因此,如果它 tell
是一个分支中的值,后来被回溯出去,则不可能“解开”该值,因此 Writer
的结果将更像是解析 算法 而不是 Parsec 呈现的数据流抽象的踪迹。它不是没用,但它很奇怪,如果 WriterT
在外面而 Parsec
在里面,你会有更自然的语义。
同样的论点适用于 State
。 Parsec 公开了它自己的用户状态功能(u
参数)和一个继承底层 monad 状态功能的 MonadState
实例。后者带有与 MonadWriter
相同的注意事项——状态遵循算法的轨迹,而不是数据流。我不知道为什么包含此实例而 Writer
实例不包含;他们都以同样的方式棘手。
-- I'm presuming the user might want a separate, non-backtracking
-- state aside from the Parsec user state.
instance (MonadState s m) => MonadState s (ParsecT s' u m) where
get = lift get
put = lift . put
Parsec 的用户状态,另一方面,遵循数据流,而不是计算,它与将 StateT
放在外部具有相同的效果。 Parsec 提供自己的状态工具而不是仅仅要求我们使用转换器,这似乎总是很奇怪——但是,现在想想,我怀疑这只是为了避免在你需要的时候把 lift
到处都是碰巧使用状态。
总而言之,他们可以提供您要求的 MonadWriter
实例,但我认为有充分的理由不提供 - 以阻止犯我认为您可能正在犯的错误。