简体   繁体   English

monad 的 monad 转换器在 Haskell 中是唯一的吗?

[英]Is the monad transformer of a monad unique in Haskell?

There have been a couple of questions (eg this and this ) asking whether every monad in Haskell (other than IO) has a corresponding monad transformer.有几个问题(例如thisthis )询问 Haskell 中的每个 monad(IO 除外)是否都有相应的 monad 转换器。 Now I would like to ask a complementary question.现在我想问一个补充问题。 Does every monad have exactly one transformer (or none as in the case of IO) or can it have more than one transformer?每个 monad 是否只有一个转换器(或者在 IO 的情况下没有转换器)还是可以有多个转换器?

A counterexample would be two monad transformers that would produce monads behaving identically when applied to the identity monad would but would produce differently behaving monads when applied to some other monad.一个反例是两个 monad 转换器,它们在应用于 identity monad 时会产生行为相同的 monad,但在应用于其他 monad 时会产生不同行为的 monad。 If the answer is that a monad can have more than one transformer I would like to have a Haskell example which is as simple as possible.如果答案是一个 monad 可以有多个转换器,我想要一个尽可能简单的 Haskell 示例。 These don't have to be actually useful transformers (though that would be interesting).这些不一定是真正有用的变压器(尽管那会很有趣)。

Some of the answers in the linked question seemed to suggest that a monad could have more than one transformer.链接问题中的一些答案似乎表明一个 monad 可以有多个转换器。 However, I don't know much category theory beyond the basic definition of a category so I wasn't sure whether they are an answer to this question.但是,除了类别的基本定义之外,我对类别理论了解不多,所以我不确定它们是否是这个问题的答案。

Here's one idea for a counterexample to uniqueness.这是唯一性的反例的一个想法。 We know that in general, monads don't compose... but we also know that if there's an appropriate swap operation, you can compose them[1]!我们知道,一般来说,monad 不会组合......但我们知道,如果有适当的swap操作,您可以组合它们[1]! Let's make a class for monads that can swap with themselves.让我们为可以与自己交换的 monad 创建一个类。

-- | laws (from [1]):
-- swap . fmap (fmap f) = fmap (fmap f) . swap
-- swap . pure = fmap pure
-- swap . fmap pure = pure
-- fmap join . swap . fmap (join . fmap swap) = join . fmap swap . fmap join . swap
class Monad m => Swap m where
    swap :: m (m a) -> m (m a)

instance Swap Identity where swap = id
instance Swap Maybe where
    swap Nothing = Just Nothing
    swap (Just Nothing) = Nothing
    swap (Just (Just x)) = Just (Just x)

Then we can build a monad transformer that composes a monad with itself, like so:然后我们可以构建一个 monad 转换器,它与自身组成一个 monad,如下所示:

newtype Twice m a = Twice (m (m a))

Hopefully it should be obvious what pure and (<$>) do.希望pure(<$>)的作用应该很明显。 Rather than defining (>>=) , I'll define join , as I think it's a bit more obvious what's going on;我将定义join ,而不是定义(>>=) ,因为我认为发生了什么更明显; (>>=) can be derived from it. (>>=)可以从中得出。

instance Swap m => Monad (Twice m) where
    join = id
        . Twice                        -- rewrap newtype
        . fmap join . join . fmap swap -- from [1]
        . runTwice . fmap runTwice     -- unwrap newtype

instance MonadTrans Twice where lift = Twice . pure

I haven't checked that lift obeys the MonadTrans laws for all Swap instances, but I did check them for Identity and Maybe .我还没有检查lift是否符合所有Swap实例的MonadTrans法律,但我确实检查了它们的IdentityMaybe

Now, we have现在,我们有

IdentityT Identity ~= Identity ~= Twice Identity
IdentityT Maybe    ~= Maybe   !~= Twice Maybe

which shows that IdentityT is not a unique monad transformer for producing Identity .这表明IdentityT不是用于生成Identity的唯一 monad 转换器。

[1] Composing monads by Mark P. Jones and Luc Duponcheel [1] Mark P. Jones 和 Luc Duponcheel 组成单子

The identity monad has at least two monad transformers: the identity monad transformer and the codensity monad transformer .恒等单子至少有两个单子转换器:恒等单子转换器和共密度单子转换器

newtype IdentityT m a = IdentityT (m a)
newtype Codensity m a = Codensity (forall r. (a -> m r) -> m r)

Indeed, considering Codensity Identity , forall r. (a -> r) -> r事实上,考虑到Codensity Identityforall r. (a -> r) -> r forall r. (a -> r) -> r is isomorphic to a . forall r. (a -> r) -> ra同构。

These monad transformers are quite different.这些单子变换器是完全不同的。 One example is that "bracket" can be defined as a monadic action in Codensity :一个例子是“括号”可以定义为Codensity中的一元动作:

bracket :: Monad m => m () -> m () -> Codensity m ()
bracket before after = Codensity (\k -> before *> k () *> after)

whereas transposing that signature to IdentityT doesn't make much sense而将该签名转换为IdentityT并没有多大意义

bracket :: Monad m => m () -> m () -> IdentityT m ()  -- cannot implement the same functionality

Other examples can be found similarly from variants of the continuation/codensity monad, though I don't see a general scheme yet.其他例子可以从 continuation/codensity monad 的变体中类似地找到,尽管我还没有看到一个通用的方案。

The state monad corresponds to the state monad transformer and to the composition of Codensity and ReaderT :状态单子对应于状态单子转换器以及CodensityReaderT的组合:

newtype StateT s m a = StateT (s -> m (s, a))
newtype CStateT s m a = CStateT (Codensity (ReaderT s m) a)

The list monad corresponds to at least three monad transformers, not including the wrong one:列表 monad 至少对应三个 monad 转换器,不包括错误的一个:

newtype ListT m a = ListT (m (Maybe (a, ListT m a)))  -- list-t
newtype LogicT m a = LogicT (forall r. (a -> m r -> m r) -> m r -> m r)  -- logict
newtype MContT m a = MContT (forall r. Monoid r => (a -> m r) -> m r))

The first two can be found respectively in the packages list-t (also in an equivalent form in pipes ), and logict .前两个可以分别在包list-t (也在管道中的等效形式)和logict中找到。

There is another example of a monad that has two inequivalent transformers: the "selection" monad.还有另一个具有两个不等价转换器的 monad 示例:“selection”monad。

 type Sel r a = (a -> r) -> a

The monad is not well known but there are papers that mention it. monad 并不为人所知,但有一些论文提到了它。 Here is a package that refers to some papers:这是引用一些论文的 package:

https://hackage.haskell.org/package/transformers-0.6.0.4/docs/Control-Monad-Trans-Select.html https://hackage.haskell.org/package/transformers-0.6.0.4/docs/Control-Monad-Trans-Select.html

That package implements one transformer: package 实现了一个变压器:

type SelT r m a = (a -> m r) -> m a

But there exists a second transformer:但是存在第二个变压器:

type Sel2T r m a = (m a -> r ) -> m a

Proving laws for this transformer is more difficult but I have done it.证明这个变压器的定律比较困难,但我已经做到了。

An advantage of the second transformer is that it is covariant in m , so the hoist function can be defined:第二个变压器的一个优点是它在m中是协变的,因此可以定义hoist function:

 hoist :: (m a -> n a) -> Sel2T r m a -> Sel2T r n a

The second transformer is "fully featured", has all lifts and "hoist".第二台变压器“功能齐全”,具有所有升降机和“提升机”。 The first transformer is not fully featured;第一个变压器功能不全; for example, you cannot define blift:: Sel r a -> SelT r ma .例如,您不能定义blift:: Sel r a -> SelT r ma In other words, you cannot embed monadic computations from Sel into SelT , just like you can't do that with the Continuation monad and the Codensity monad.换句话说,您不能将来自Sel的单子计算嵌入到SelT中,就像您不能对 Continuation monad 和 Codensity monad 那样做。

But with the Sel2T transformer, all lifts exist and you can embed Sel computations into Sel2T .但是使用Sel2T转换器,所有提升都存在,您可以将Sel计算嵌入到Sel2T中。

This example shows a monad with two transformers without using the Codensity construction in any way.这个例子展示了一个带有两个转换器的 monad,没有以任何方式使用代码密度结构。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM