Search code examples
haskellgeneric-programminghigher-kinded-types

Pushing applicative effects into HKD fields with Barbies or Higgledy


I am using barbies-th to turn a record type into a higher-kinded datatype:

declareBareB [d|
  data Foo = MkFoo
      { field1 :: Int
      , field2 :: Bool
      } |]

I can then write a function to push any applicative effect into individual fields:

bdistribute :: (Applicative f) => f (Foo Bare Identity) -> Foo Covered f
bdistribute foo = MkFoo
    { field1 = field1 <$> foo
    , field2 = field2 <$> foo
    }

But it feels like I should be able to write bdistribute once and for all for all Barbie-style HKDs. In other words, I am looking for the dual of Higgledy's construct. Higgledy has these two methods in the Construct typeclass:

construct :: HKD structure f -> f structure
deconstruct :: structure -> HKD structure f

but I'd like

nstruct :: (Applicative f) => f structure -> HKD structure f

Brainstorming below: one idea I just had is that this problem can be simplified into writing the following function generically:

shape :: Foo Covered ((->) (Foo Bare Identity))
shape = MkFoo
    { field1 = field1
    , field2 = field2
    }

since then we have

bdistribute :: (Applicative f) => f (Foo Bare Identity) -> Foo Covered f
bdistribute = bmap (<$> x) shape

More generally, from shape we can also write

bdistribute :: (Functor f, Applicative g, ApplicativeB b, TraversableB b) => f (b g) -> b (Compose f g)
bdistribute x = bmap (\f -> Compose $ fmap f . bsequence' <$> x) shape

Solution

  • I ended up adding a DistributiveB typeclass to the Barbies package, available since 2.0.1.0:

    class FunctorB b => DistributiveB (b :: (k -> Type) -> Type) where
        bdistribute :: Functor f => f (b g) -> b (Compose f g)
    

    To recover the shape function from my original question, we can use the following specialized version of bdistribute:

    bdecompose :: DistributiveB b => (a -> b Identity) -> b ((->) a)
    

    This gives us shape = bdecompose id