具有默认值的相互递归定义的类型类方法
Mutually recursively defined typeclass methods with defaults
我想定义一个具有两种方法的类型类,其中实现任何一种方法就足够了(但如果需要,您可以独立实现这两种方法)。这种情况与 Eq
中的情况相同,其中 x == y = not (x /= y)
和 x /= y = not (x == y)
。到目前为止一切顺利,我也可以做同样的事情:
class (FunctorB b) => DistributiveB (b :: (Type -> Type) -> Type) where
bdistribute :: (Distributive f) => f (b g) -> b (Compose f g)
bdistribute x = bmap (\f -> Compose $ fmap f . bsequence' <$> x) bshape
bshape :: b ((->) (b Identity))
bshape = bdistribute' id
bdistribute' :: (DistributiveB b, Distributive f) => f (b Identity) -> b f
bdistribute' = bmap (fmap runIdentity . getCompose) . bdistribute
但是,我还想提供 bdistribute
的通用 default 实现,如果 bdistribute
没有定义,我可以这样做:
class (FunctorB b) => DistributiveB (b :: (Type -> Type) -> Type) where
bdistribute :: (Distributive f) => f (b g) -> b (Compose f g)
default bdistribute
:: forall f g
. CanDeriveDistributiveB b f g
=> (Distributive f) => f (b g) -> b (Compose f g)
bdistribute = gbdistributeDefault
bshape :: b ((->) (b Identity))
bshape = bdistribute' id
但是,一旦我想两者都做,它就坏了:
class (FunctorB b) => DistributiveB (b :: (Type -> Type) -> Type) where
bdistribute :: (Distributive f) => f (b g) -> b (Compose f g)
bdistribute x = bmap (\f -> Compose $ fmap f . bsequence' <$> x) bshape
default bdistribute
:: forall f g
. CanDeriveDistributiveB b f g
=> (Distributive f) => f (b g) -> b (Compose f g)
bdistribute = gbdistributeDefault
bshape :: b ((->) (b Identity))
bshape = bdistribute' id
出现以下错误消息:
Conflicting definitions for bdistribute
现在,我明白了这个错误的意义所在;而且,我认为我想要的也是合理且定义明确的:如果您手写 DistributiveB
实例,您可以覆盖 bdistribute
and/or bshape
,但是如果你只写 instance DistributiveB MyB
那么你会得到 bshape
根据 bdistribute
和 bdistribute
定义的 gdistributeDefault
.
一个妥协是放弃第一个默认定义:当用户手动实施 bshape
时,要求添加一行以获得另一个 "default" 实施并不过分bdistribute
来自它。
class FunctorB b => DistributiveB b where
bdistribute :: Distributive f => f (b g) -> b (Compose f g)
default bdistribute :: CanDeriveDistributiveB b f g => ...
bdistribute = ...
bshape :: b ((->) (b Identity))
bshape = ...
-- Default implementation of bdistribute with an explicitly defined bshape
bdistributeDefault :: DistributiveB b => f (b g) -> b (Compose f g)
bdistributeDefault x = bmap (\f -> Compose $ fmap f . bsequence' <$> x) bshape
所以实例看起来像这样:
-- Generic default
instance DistributiveB MyB
-- Manual bshape
instance DistributiveB MyB where
distribute = distributeDefault -- one extra line of boilerplate
bshape = ... -- custom definition
-- Manual distribute
instance DistributiveB MyB where
distribute = ...
-- bshape has a default implementation
我想定义一个具有两种方法的类型类,其中实现任何一种方法就足够了(但如果需要,您可以独立实现这两种方法)。这种情况与 Eq
中的情况相同,其中 x == y = not (x /= y)
和 x /= y = not (x == y)
。到目前为止一切顺利,我也可以做同样的事情:
class (FunctorB b) => DistributiveB (b :: (Type -> Type) -> Type) where
bdistribute :: (Distributive f) => f (b g) -> b (Compose f g)
bdistribute x = bmap (\f -> Compose $ fmap f . bsequence' <$> x) bshape
bshape :: b ((->) (b Identity))
bshape = bdistribute' id
bdistribute' :: (DistributiveB b, Distributive f) => f (b Identity) -> b f
bdistribute' = bmap (fmap runIdentity . getCompose) . bdistribute
但是,我还想提供 bdistribute
的通用 default 实现,如果 bdistribute
没有定义,我可以这样做:
class (FunctorB b) => DistributiveB (b :: (Type -> Type) -> Type) where
bdistribute :: (Distributive f) => f (b g) -> b (Compose f g)
default bdistribute
:: forall f g
. CanDeriveDistributiveB b f g
=> (Distributive f) => f (b g) -> b (Compose f g)
bdistribute = gbdistributeDefault
bshape :: b ((->) (b Identity))
bshape = bdistribute' id
但是,一旦我想两者都做,它就坏了:
class (FunctorB b) => DistributiveB (b :: (Type -> Type) -> Type) where
bdistribute :: (Distributive f) => f (b g) -> b (Compose f g)
bdistribute x = bmap (\f -> Compose $ fmap f . bsequence' <$> x) bshape
default bdistribute
:: forall f g
. CanDeriveDistributiveB b f g
=> (Distributive f) => f (b g) -> b (Compose f g)
bdistribute = gbdistributeDefault
bshape :: b ((->) (b Identity))
bshape = bdistribute' id
出现以下错误消息:
Conflicting definitions for
bdistribute
现在,我明白了这个错误的意义所在;而且,我认为我想要的也是合理且定义明确的:如果您手写 DistributiveB
实例,您可以覆盖 bdistribute
and/or bshape
,但是如果你只写 instance DistributiveB MyB
那么你会得到 bshape
根据 bdistribute
和 bdistribute
定义的 gdistributeDefault
.
一个妥协是放弃第一个默认定义:当用户手动实施 bshape
时,要求添加一行以获得另一个 "default" 实施并不过分bdistribute
来自它。
class FunctorB b => DistributiveB b where
bdistribute :: Distributive f => f (b g) -> b (Compose f g)
default bdistribute :: CanDeriveDistributiveB b f g => ...
bdistribute = ...
bshape :: b ((->) (b Identity))
bshape = ...
-- Default implementation of bdistribute with an explicitly defined bshape
bdistributeDefault :: DistributiveB b => f (b g) -> b (Compose f g)
bdistributeDefault x = bmap (\f -> Compose $ fmap f . bsequence' <$> x) bshape
所以实例看起来像这样:
-- Generic default
instance DistributiveB MyB
-- Manual bshape
instance DistributiveB MyB where
distribute = distributeDefault -- one extra line of boilerplate
bshape = ... -- custom definition
-- Manual distribute
instance DistributiveB MyB where
distribute = ...
-- bshape has a default implementation