为什么runXXX不是MonadTrans定义的一部分?

时间:2014-07-15 03:31:47

标签: haskell monad-transformers

MonadTrans文件说:

  

每个monad变换器还带有一个runXXX操作来打开变换器,暴露内部monad的计算。

所以我想知道为什么MonadTrans没有被定义为

class MonadTrans t where
  type p :: *
  type r :: * -> *
  lift:: Monad m => m a -> t m a
  run :: t m a -> p -> m (r a)

消除上述条款?那上面的定义不够通用吗?如果是这样,哪个monad变压器不适合这个定义?

更新

调整一点以启用不同的结果类型。

2 个答案:

答案 0 :(得分:15)

没有用于运行monad变换器的通用接口。例如,尝试使用您的界面运行LogicTContTFreeT

即使你可以概括你的界面来处理所有这些例子,你仍然会错过关键因素:法律。类型类方法应遵循允许您在不咨询特定实例源的情况下推断使用类型类接口的代码的等式。例如,来自lift的{​​{1}}方法必须遵守这些法律:

MonadTrans

理论上有理由lift (return x) = return x lift (m >>= f) = lift m >>= \x -> lift (f x) 应该遵守这些规律,如果你用这种无点的方式写法则会更加明显:

lift

换句话说,(lift .) return = return -- fmap id = id (lift .) (f >=> g) = (lift .) f >=> (lift .) g -- fmap (f . g) = fmap f . fmap g 是两个kleisli类别之间的仿函数,因此(lift .)是monad态射。

很多人都在考虑定义类型类,例如liftFunctorMonad,而Typeclassopedia是开始更多地了解此主题的好地方

答案 1 :(得分:0)

我一次又一次地想到这一点,除了接受的答案之外,我还想添加更多评论。

正如我们目前所见,要为 ALL 可能run定义MonadTrans界面并不容易。但是只有一种实现可以保证能够适应所有情况:

class MonadTrans' t where
    type r m a :: *
    lift':: Monad m => m a -> t m a
    run :: Monad m => t m a => r m a

乍一看似乎解决了这个问题......

但等一下,MonadTrans'实施者可以对待:

instance MonadTrans t => MonadTrans' t where
   type r = t
   lift' = lift
   run = id

所以实际上这个定义并没有限制任何东西,关于runXXX的条款无法消除......

无论如何,run上面的方式实际上有一个有趣的用法。在this post中,作者声称Haskell中只有一个类型类足以拥有所有(大多数?)类型类的好处,如重载等。

class C l t | l -> t where ac :: l -> t