jcpetruzza / barbies

BSD 3-Clause "New" or "Revised" License
92 stars 15 forks source link

I like to barbie so I tried to barbie while I barbied; but I got a type error. #47

Closed silky closed 6 months ago

silky commented 8 months ago

I'm having trouble trying to wrap a barbie in a barbie, I think; but to me it's something htat seems pretty reasonable.

Consider:

data A' t f = A
    { name   :: Wear t f Int
    , thing  :: Wear t f [Wear t f Int]
    }
    deriving (Generic)

instance FunctorB (A' Bare)
instance FunctorB (A' Covered)

There is a type-error on the Covered instance:

<interactive>:7:10: error:
    • No instance for (barbies-2.0.4.0:Barbies.Generics.Functor.GFunctor
                         0
                         f
                         g
                         (Rec (barbies-2.0.4.0:Data.Generics.GenericN.Param 0 f [barbies-2.0.4.0:Data.Generics.GenericN.Param 0 f Int]) (f [f Int]))
                         (Rec (barbies-2.0.4.0:Data.Generics.GenericN.Param 0 g [barbies-2.0.4.0:Data.Generics.GenericN.Param 0 g Int]) (g [g Int])))
        arising from a use of ‘barbies-2.0.4.0:Barbies.Internal.FunctorB.$dmbmap’
    • In the expression: barbies-2.0.4.0:Barbies.Internal.FunctorB.$dmbmap @(*) @(A' Covered)
      In an equation for ‘bmap’: bmap = barbies-2.0.4.0:Barbies.Internal.FunctorB.$dmbmap @(*) @(A' Covered)
      In the instance declaration for ‘FunctorB (A' Covered)’

I'm not too sure how to go about solving this. Any advice would be appreciated!

Some more context: https://discourse.haskell.org/t/trouble-with-barbies/8344/4

silky commented 8 months ago

Maybe this part of the documentation is relevant - https://hackage.haskell.org/package/barbies-2.0.0.0/docs/Barbies.html#g:3 - but the example doesn't compile, and the link to "see more" is empty 😅

silky commented 8 months ago
data Person f

newtype Dependants f' f
  = Dependants { getDependants :: f' [Person f] }
  deriving (Generic)

instance Functor f' => FunctorB (Dependants f')
instance FunctorT Dependants

-- type Dependants f = Dependants f f
<interactive>:4:34: error:
    • Couldn't match kind ‘*’ with ‘k0 -> *’
      Expected kind ‘(k0 -> *) -> *’, but ‘Dependants f'’ has kind ‘* -> *’
    • In the first argument of ‘FunctorB’, namely ‘(Dependants f')’
      In the instance declaration for ‘FunctorB (Dependants f')’
silky commented 8 months ago

Solved:

newtype Thing (f :: * -> *) =  Thing { getThing :: [f Int] }
  deriving (Generic, Typeable)

instance FunctorB Thing

deriving instance (Show (f Int)) => Show (Thing f)
deriving instance (Eq (f Int))   => Eq (Thing f)

data A' f' f = A
  { name  :: f' String
  , thing :: f' (Thing f) }
  deriving (Generic)

instance FunctorT A'
instance Functor f' => FunctorB (A' f')

deriving instance (Show (Thing f), Show (f' (Thing f)), Show (f' String)) => Show (A' f' f)

type A f = A' f f

a :: A Identity
a = A (pure "name") (pure $ Thing [pure 1, pure 2, pure 3])

b :: A Maybe
b = btmap1 (Just . runIdentity) a
silky commented 8 months ago

The trouble is that only works at one level, if you then go and embed A' in another barbie in a similar style things break again:

newtype Thing (f :: * -> *) =  Thing { getThing :: [f Int] }
  deriving (Generic, FunctorB)

deriving instance (Show (f Int)) => Show (Thing f)

data A' f' f = A
  { name  :: f' String
  , thing :: f' (Thing f)
  }
  deriving (Generic)

instance FunctorT A'
instance Functor f' => FunctorB (A' f')

deriving instance (Show (f' (Thing f)), Show (f' String)) => Show (A' f' f)

newtype B' f'' f' f = B
  { someA :: f'' (A' f' f)
  }
  deriving Generic

type A f = A' f f
type B f = B' f f f

a :: A Identity
a = A (pure "name") (pure $ Thing [pure 1, pure 2, pure 3])

a' :: A Maybe
a' = btmap1 (Just . runIdentity) a

--b :: B Identity
--b = B a

The trouble is how to define the FunctorT/B instances; it just doesn't seem there's a way.

instance FunctorT B'

is clearly wrong, as B' has the wrong kind.

What are the options here?

Thanks!

jcpetruzza commented 8 months ago

Hi @silky, sorry for the late reply. As you probably noticed, it is not possible to have a FunctorB instance for something like:

data Foo f = Foo (f [f Int]))

This is not a limitation of the library, but it's just not possible to write a manual instance either. If you try, you'll end up wth something similar to:

instance FunctorB Foo where
  bmap h (Foo ffis) = Foo (h <$> h ffis)  -- doesn't work since h :: (forall x. f a -> g a), and you don't have a Functor instance for g

If you only need one level of this, then using btmap1, as you saw, does the trick. But this doesn't generalize to more levels.

edit: removed alternative suggestion that wouldn't really work

silky commented 6 months ago

Thanks for the response @jcpetruzza .

I'm revisiting this now and still looking around for an answer :) I tried barbies-layered but I didn't have much luck (there's no default bmap implementation and I wasn't sure what to write); now looking at the hypertypes library.

Would be lovely if there was a solution :)

jcpetruzza commented 6 months ago

As I said, I don't think you'll be able to make this work given the type of bmap.

One could define a different class hierarchy, e.g one where bmap requires a Functor constraint on the input or the output, and you'd be able to define your instances there, but that'd come with other trade-offs.