作为Haskell自学练习的一部分,我正在尝试推导出一个叫做 Monad
我的类型的实例。该类型定义为
newtype ParsePackUnpack f a = ParsePackUnpack
{
unparse:: State PackUnpackState (Ap f a)
}
其中 Ap f a
来自 Data.Monoid
. 对于我的类型,我想说的是,解析是一个有状态的操作,结果是任何一个 monoid
.
到目前为止,我已经成功实施了 Functor
和 Applicative
实例,但我无法为我的类型正确导出一个Monad实例。
instance Functor f => Functor (ParsePackUnpack f) where
fmap f ma =
let f' = fmap f -- lift (a -> b) to (Ap f a -> Ap f b)
in ParsePackUnpack $ f' <$> (unparse ma)
instance Applicative f => Applicative (ParsePackUnpack f) where
pure = ParsePackUnpack . pure . pure
f <*> ma =
let f' = liftA2 (<*>) . unparse $ f -- lift Ap f (a -> b) -> Ap f a -> Ap f b to State s (Ap f a) -> State s (Ap f b)
in ParsePackUnpack $ f' (unparse ma) -- Apply to State s (Ap f a)
但是我不能正确地导出一个Monad实例给我的类型。经过一些类型分析,这是我最新的尝试。
instance Monad f => Monad (ParsePackUnpack f) where
return = ParsePackUnpack . return . return
ma >>= f = ParsePackUnpack . state $ \st ->
let (a, s) = runState (unparse ma) st
res = a >>= fst . flip runState s . unparse . f -- fst ignores state from the result
in (res, s)
我相信这是不正确的,因为我忽略了状态的存在 res
的操作。
正确的实现方式是什么?>>=
的操作?由于这是一个学习的过程,我试图避免使用Monad变换器。如果 Monad 变换器是最好的方法,您能解释一下为什么会这样吗?
Monad不像applicatives那样好编译。而 f (g a)
是适用性的,只要 f
和 g
是(因此你有能力写出应用实例),它一般不是一个单体,当 f
和 g
是单项式。这就是为什么我们需要单项式变换器而不需要应用式变换器的原因。
下面是一个相关的练习。 忘记使用 State
从库中取出,让我们手动处理它的表示。State s (IO a)
展开到 s -> (IO a, s)
. 要实现bind,你将得到
f :: s -> (IO a, s)
g :: a -> s -> (IO b, s)
你能不能想出如何将第一种喂给第二种,通过。s
通过 "statefully"?
bound :: s -> (IO b, s)
bound s0 = ??
给它一个尝试。还有 (破坏者) 在你说服自己这是不可能的之后,想一想是什么使它不可能,以及你需要如何修改类型来使它成为可能。 然后用这个模式来定义一个"StateIO s
"单子。