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变压器不适合这个定义?
更新
调整一点以启用不同的结果类型。
答案 0 :(得分:15)
没有用于运行monad变换器的通用接口。例如,尝试使用您的界面运行LogicT
或ContT
或FreeT
。
即使你可以概括你的界面来处理所有这些例子,你仍然会错过关键因素:法律。类型类方法应遵循允许您在不咨询特定实例源的情况下推断使用类型类接口的代码的等式。例如,来自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态射。
很多人都在考虑定义类型类,例如lift
,Functor
和Monad
,而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