[英]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
的通用默認實現,如果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
。
一個折衷方案是放棄第一個默認定義:當用戶手動實現bshape
時,要求添加一行以從中獲取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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.