为嵌套的 monadic 类型实现 Monad 实例
Implementing Monad instance for a nested monadic type
作为 Haskell 自学练习的一部分,我正在尝试为我的类型导出一个 Monad
实例。类型定义为:
newtype ParsePackUnpack f a = ParsePackUnpack
{
unparse:: State PackUnpackState (Ap f a)
}
其中 Ap f a
来自 Data.Monoid
。对于我的类型,我试图说解析是一个有状态的操作,结果是任何 monoid
.
到目前为止,我已经成功地通过提升实现了这个 3 级深度类型的 Functor
和 Applicative
实例:
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 的组合不如应用程序好。虽然 f (g a)
是应用程序,只要 f
和 g
是(因此您可以编写应用程序实例),但当 f
和 [=14 时,它通常不是 monad =] 是单子。这就是为什么我们需要 monad 转换器而不是应用转换器。
这是一个相关的练习。忘记使用库中的 State
,让我们手动处理它的表示。 State s (IO a)
展开为 s -> (IO a, s)
。要实现绑定,您将获得
f :: s -> (IO a, s)
g :: a -> s -> (IO b, s)
你能想出如何将第一个喂给第二个,通过 "statefully" 传递 s
吗?
bound :: s -> (IO b, s)
bound s0 = ??
试一试。 (剧透) 在你确信这是不可能的之后,想想是什么让它不可能,以及你需要如何修改类型才能让它成为可能。然后使用该模式定义一个“StateIO s
”monad。
作为 Haskell 自学练习的一部分,我正在尝试为我的类型导出一个 Monad
实例。类型定义为:
newtype ParsePackUnpack f a = ParsePackUnpack
{
unparse:: State PackUnpackState (Ap f a)
}
其中 Ap f a
来自 Data.Monoid
。对于我的类型,我试图说解析是一个有状态的操作,结果是任何 monoid
.
到目前为止,我已经成功地通过提升实现了这个 3 级深度类型的 Functor
和 Applicative
实例:
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 的组合不如应用程序好。虽然 f (g a)
是应用程序,只要 f
和 g
是(因此您可以编写应用程序实例),但当 f
和 [=14 时,它通常不是 monad =] 是单子。这就是为什么我们需要 monad 转换器而不是应用转换器。
这是一个相关的练习。忘记使用库中的 State
,让我们手动处理它的表示。 State s (IO a)
展开为 s -> (IO a, s)
。要实现绑定,您将获得
f :: s -> (IO a, s)
g :: a -> s -> (IO b, s)
你能想出如何将第一个喂给第二个,通过 "statefully" 传递 s
吗?
bound :: s -> (IO b, s)
bound s0 = ??
试一试。 (剧透) 在你确信这是不可能的之后,想想是什么让它不可能,以及你需要如何修改类型才能让它成为可能。然后使用该模式定义一个“StateIO s
”monad。