Я хотел бы определить класс типов, который имеет два метода, где достаточно реализовать любой (но вы можете реализовать оба независимо, если это необходимо). Эта ситуация такая же, как в 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
Однако я также хотел бы предоставить обобщенную c стандартную реализацию bdistribute
, которую я могу сделать, если 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
со следующим сообщением об ошибке:
Противоречивые определения для bdistribute
Теперь я вижу, как эта ошибка имеет смысл; но я думаю, что то, что я хотел бы, также разумно и четко определено: если вы пишете вручную свой экземпляр DistributiveB
, вы можете переопределить bdistribute
и / или bshape
, но если вы просто напишите instance DistributiveB MyB
, тогда вы получите bshape
, определенный в терминах bdistribute
и bdistribute
, определенный из gdistributeDefault
.