我正在玩here给出的精彩答案。我天真地期待这个工作:
{-# LANGUAGE MultiParamTypeClasses #-}
import Data.Functor.Compose
import Control.Monad
class (Functor f, Functor g) => Adjoint f g where
counit :: f (g a) -> a
unit :: a -> g (f a)
instance (Adjoint f g) => Monad (Compose g f) where
return x = Compose $ unit x
x >>= f = Compose . fmap counit . getCompose $ fmap (getCompose . f) x
但是,它并没有。我收到以下错误:
adjoint.hs:10:10: error:
• Could not deduce (Applicative g)
arising from the superclasses of an instance declaration
这里似乎正在发生什么。 GHC要求所有Monad
具有Applicative
实例,因此编译器会为Compose g f
寻找一个实例。
Data.Functor.Compose
定义了这样的实例,但它需要g
(和f
)为Applicative
:
instance (Applicative f, Applicative g) => Applicative (Compose f g) where
pure x = Compose (pure (pure x))
Compose f <*> Compose x = Compose ((<*>) <$> f <*> x)
但在一般情况下,Compose g f
可以是Monad
(从而Applicative
),即使g
和f
都不是Applicative
f
。通常的示例是(,) s
为g
时,(->) s
为Compose g f
。然后State
是Monad
(,) s
,即使Applicative
不是(始终)Adjoint
。
这似乎有点不理想。首先,使用Monad
为两个Functor
定义Applicative
个实例并不是Functors
会很好。但更一般地说,有两种Applicative
的组合方式为Data.Functor.Compose
,即使其中一个或两个都不成功。但是,目前GHC的行为方式与Applicative
的设置方式相结合,使您无法实现这些用例。如果您尝试为任何Compose g f
定义Data.Functor.Compose
实例,GHC将会抱怨重复的实例声明。
显而易见的解决方案就是滚动您自己的Applicative
版本并废弃Bool ? String? : String
行。这有点直截了当,如果有点hacky。还有其他更有原则性的方法来解决这个问题吗?
答案 0 :(得分:4)
通常,如果您需要其他实例,则需要新类型。应该非常简单;只需几行代码:
newtype AdjointCompose f g a = AdjointCompose { runAdjointCompose :: f (g a) }
instance (Functor f, Functor g) => Functor (AdjointCompose f g) where
fmap = liftM
instance Adjoint f g => Applicative (AdjointCompose g f) where
pure = return
(<*>) = ap
instance Adjoint f g => Monad (AdjointCompose g f) where
-- as in your proposed instance
现在你可以吃蛋糕并吃掉它:当你想要Applicative
的组合时,使用Compose
,当你想要Applicative
时,你可以从伴随中获得,使用AdjointCompose
。
如果您想要Compose
免费定义的其他一些实例,您可以将AdjointCompose
写为Compose
上的新类型,然后通过GeneralizedNewtypeDeriving
获取它们。< / p>