簡體   English   中英

具有默認值的相互遞歸定義的類型類方法

[英]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定義的bdistributegdistributeDefault

一個折衷方案是放棄第一個默認定義:當用戶手動實現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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM