GithubHelp home page GithubHelp logo

barbies's People

Contributors

alexpeits avatar chrispenner avatar danwdart avatar fumieval avatar gergoerdi avatar intolerable avatar jackkelly-bellroy avatar jcpetruzza avatar lspitzner avatar peddie avatar vapourismo avatar williamyaoh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

barbies's Issues

`ConstraintsB` can't be derived for data family?

Hello again 😅 Long time no see!

A slightly weird use case: this fails to derive ConstraintsB (but notably is fine with just FunctorB) because of a failure to deduce some Coercible instance.

import Barbies qualified as B

data family T x :: (Type -> Type) -> Type
data instance T () f = C { s :: f Bool }
  deriving stock Generic
  deriving anyclass (B.FunctorB, B.ConstraintsB)

Weirder still, the error suggests that GHC made no progress on Zip at all here, getting immediately stuck at the datatype metadata level:

...: error:
    • Could not deduce: Coercible
                          (barbies-2.0.4.0:Data.Generics.GenericN.Zip
                             (Rep
                                (T (barbies-2.0.4.0:Data.Generics.GenericN.Param 1 ())
                                   (barbies-2.0.4.0:Data.Generics.GenericN.Param 0 f)))
                             (G.D1
                                ('G.MetaData "T" "Main" "main" 'False)
                                (G.C1
                                   ('G.MetaCons "C" 'G.PrefixI 'True)
                                   (G.S1
                                      ('G.MetaSel
                                         ('Just "s")
                                         'G.NoSourceUnpackedness
                                         'G.NoSourceStrictness
                                         'G.DecidedLazy)
                                      (G.Rec0 (f Bool))))))
                          (G.Rec0 (f Bool))
        arising from the 'deriving' clause of a data type declaration
      from the context: B.AllB c (T ())
        bound by the deriving clause for ‘B.ConstraintsB (T ())’
        at ...
    • When deriving the instance for (B.ConstraintsB (T ()))
   |
68 |   deriving anyclass (B.FunctorB, B.ConstraintsB)
   |                                  ^^^^^^^^^^^^^^

I don't understand enough of the Barbies internals to give a good guess as to why, so before I do any rummaging, do you have any idea? Otherwise, I'll see whether I can figure it out.

Thanks!
Tom

Field name API?

Being able to obtain field names of a record is pretty useful. The following code works but unfortunately GHC can't derive Generic1 instances of higher-kinded types... Do you think barbies' hacks can be applied here?

class FieldNamesB b where
  bfieldNames :: b (Const String)

instance FieldNamesB U1 where
  bfieldNames = U1

instance (FieldNamesB f, FieldNamesB g) => FieldNamesB (f :*: g) where
  bfieldNames = bfieldNames :*: bfieldNames

instance (FieldNamesB t, c ~ 'MetaSel ('Just name) su ss dl, KnownSymbol name, t ~ Const String) => FieldNamesB (M1 S c t) where
  bfieldNames = M1 (Const (fromString (symbolVal (Proxy :: Proxy name))))

instance (FieldNamesB t) => FieldNamesB (M1 C c t) where
  bfieldNames = M1 bfieldNames

instance (FieldNamesB t) => FieldNamesB (M1 D c t) where
  bfieldNames = M1 bfieldNames

Compilation failure on GHC 9

src/Barbies/Internal/FunctorT.hs:50:11: error:
    • The default type signature for tmap:
        forall (f :: k -> *) (g :: k -> *) (x :: k').
        CanDeriveFunctorT t f g x =>
        (forall (a :: k). f a -> g a) -> t f x -> t g x
      does not match its corresponding non-default type signature
    • When checking the class method:
        tmap :: forall k k' (t :: (k -> *) -> k' -> *) (f :: k -> *)
                       (g :: k -> *).
                FunctorT t =>
                (forall (a :: k). f a -> g a) -> forall (x :: k'). t f x -> t g x
      In the class declaration for ‘FunctorT’
   |
50 |   default tmap
   |           ^^^^

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

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

Type errors on GHC 8.8

GHC 8.8 seem to infer quantifications differently from <8.6, resulting in type errors. I had to make the following changes to get this to compile:

haskell-vanguard@a1d2d7d

I haven't figured out how to make it work with 8.6 without resorting to CPP though.

Template Haskell helper

I made barbies-th, a wrapper library which generates strippable HKD from a normal definition.

declareBareB [d|
  data Foo = Foo
    { foo :: Int
    , bar :: String
    }  |]

becomes

data Foo sw h = Foo
    { foo :: Wear sw h Int,
    , bar :: Wear sw h String
    } deriving Generic
instance BareB Foo
instance FieldNamesB (Foo Covered) where
  bfieldNames = Foo (Const "foo") (Const "bar")
instance ProductB (Foo Covered) where
  bprod (Foo xfoo xbar) (Foo yfoo ybar)
    = Foo (Pair xfoo yfoo) (Pair xbar ybar)
instance FunctorB (Foo Covered)
instance TraversableB (Foo Covered)
instance ConstraintsB (Foo Covered)
instance ProductBC (Foo Covered)

Are you interested in introducing something like this to barbies? It does add template-haskell dependency, that's why I didn't go forward with a pull request.

Lifting Semigroup and Monoid instance

My main use case for Barbies is collecting partial output in a WriterT (out Covered Last) (State s), to be assembled to a full out Bare Identity by filling the missing fields from s. However, this requires out Covered Last to be a monoid.

Would it make sense to include the following generic lifting instance in Barbies itself, to avoid orphan instance problems? Note that Higgledy already includes the equivalent instances for HKD b f, but I'd like to move from Higgledy to Barbies + Barbies-TH (to have proper named fields).

instance (ApplicativeB (a Covered), forall b. Semigroup (f b)) => Semigroup (a Covered f) where
    (<>) = bzipWith (<>)

instance (ApplicativeB (a Covered), forall b. Monoid (f b)) => Monoid (a Covered f) where
    mempty = bpure mempty

Tests fail to build on ghc 9.2.1

Hey, getting the following:

test/TestBarbies.hs:256:19: error:
    • Non type-variable argument in the constraint: Show (Record3 f)
      (Use FlexibleContexts to permit this)
    • In the stand-alone deriving instance for
        ‘(Show (f Int), Show (Record3 f)) => Show (NestedF f)’
    |
256 | deriving instance (Show (f Int), Show (Record3 f)) => Show (NestedF f)
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

test/TestBarbies.hs:257:19: error:
    • Non type-variable argument in the constraint: Eq (Record3 f)
      (Use FlexibleContexts to permit this)
    • In the stand-alone deriving instance for
        ‘(Eq (f Int), Eq (Record3 f)) => Eq (NestedF f)’
    |
257 | deriving instance (Eq   (f Int), Eq   (Record3 f)) => Eq   (NestedF f)
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[10 of 12] Compiling TestBarbiesW     ( test/TestBarbiesW.hs, dist/build/barbies-test/barbies-test-tmp/TestBarbiesW.o )

test/TestBarbiesW.hs:271:19: error:
    • Non type-variable argument
        in the constraint: Show (Record3W Covered f)
      (Use FlexibleContexts to permit this)
    • In the stand-alone deriving instance for
        ‘(Show (f Int), Show (Record3W Covered f)) =>
         Show (NestedFW Covered f)’
    |
271 | deriving instance (Show (f Int), Show (Record3W Covered f)) => Show (NestedFW Covered f)
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

test/TestBarbiesW.hs:272:19: error:
    • Non type-variable argument
        in the constraint: Eq (Record3W Covered f)
      (Use FlexibleContexts to permit this)
    • In the stand-alone deriving instance for
        ‘(Eq (f Int), Eq (Record3W Covered f)) => Eq (NestedFW Covered f)’
    |
272 | deriving instance (Eq   (f Int), Eq   (Record3W Covered f)) => Eq   (NestedFW Covered f)
    |

Should just be a matter of turning FlexibleContexts on - it's a bug that got fixed with ghc afaik.

bzipWithCF

I'd like to write the following function:

type Mask b = b Covered (Const Bool)

mask :: (BareB b, ApplicativeB (b Covered), _ctx) => Mask b -> b Covered f -> b Covered f
mask keep = bzipWith mask1 keep
  where
    mask1 :: (Monoid m) => Const Bool a -> m -> m
    mask1 (Const keep) x = if keep then x else mempty

So here, I think a sensible solution for _ctx would be to require that all fields of f a of b Covered have a Monoid (f a) instance, i.e. that we have AllBF Monoid f (b Covered). But just using bzipWith, or bzipWithC, doesn't propagate this constraint to the per-leaf function.

I think it would make sense to have an AllBF-based version of bzipWithC, something with the following type:

bzipWithCF 
    :: (AllBF c1 f b, AllBF c2 g b, ConstraintsB b, ApplicativeB b) 
    => (forall a. (c1 (f a), c2 (g a)) => f a -> g a -> h a) 
    -> b f -> b g -> b h

What do you think?

Compilation error on GHC 9.2.2

The following code:

data T f = T (f Int) deriving (Generic, FunctorB)

Fails to build on GHC 9.2.2 for me, giving the error:

    • Couldn't match representation of type: barbies-2.0.3.1:Data.Generics.GenericN.Rec
                                               (barbies-2.0.3.1:Data.Generics.GenericN.Param
                                                  0 f Int)
                                               (f Int)
                               with that of: K1 R (f Int)
        arising from the 'deriving' clause of a data type declaration
      The data constructor ‘barbies-2.0.3.1:Data.Generics.GenericN.Rec’
        of newtype ‘barbies-2.0.3.1:Data.Generics.GenericN.Rec’
        is not in scope
    • When deriving the instance for (FunctorB T)

I think I'm following the docs close enough here and this trivial example should compile, but I'm not sure if this is a bug or just me doing something wrong.

Pathological compilation behavior with GHC 9.8.1

I've found that a module using barbies takes exponentially longer to compile under GHC 9.8 as I add fields to a data type.

The repo at https://github.com/m4dc4p/ghc98-bug demonstrates the problem. If you have GHC and cabal installed, you should be able to build with just cabal build:

$ cabal build
Warning: The package list for 'hackage.haskell.org' is 19 days old.
Run 'cabal update' to get the latest list of available packages.
Resolving dependencies...
Build profile: -w ghc-9.8.1 -O1
In order, the following will be built (use -v for more details):
 - ghc98-bug-0.0.0 (lib) (first run)
Configuring library for ghc98-bug-0.0.0..
Preprocessing library for ghc98-bug-0.0.0..
Building library for ghc98-bug-0.0.0..

Uncommenting fields on the HDKType data constructor cause compilation to take longer and longer (and use more memory):

  • 1 field - 2.5s, 198MB peak
  • 2 fields - 7s, 1.0GB peak
  • 3 fields - 26.8s, 4.6GB peak
  • 4 fields - 82.9s, 14.5GB peak

Its worth noting the removing the deriving instance for Eq causes the problem to go away (but, of course, that instance is something we want). Also worth noting that with -O0, the problem goes away. I have not narrowed down which optimization is the problem.

Laws for ProductB are overly restrictive

This issue didn't arise from any practical problem. I just studied library and noticed that laws for ProductB are overly strict. Let me explain why

Let start from Applicative:

class Functor f => Applicaitve f where
  pure :: a -> f a
  (<*>) :: f (a -> b) -> f a -> f b

But we can rewrite it in slightly different but equivalent form:

class Functor f => Applicaitve f where
  pure :: a -> f a
  (<*>) :: f a -> f a -> f (a, b)

And ProductB look exactly like second variant of Applicative except for different kind. If we look at ProductB as different-kinded applicative then Bad from documentation admits writer-like instance:

data Bad f = Bad (f String) Int

instance FunctorB Bad where
  bmap f (Bad g i) = Bad (f g) i

instance ProductB Bad where
  buniq f = Bad f 0
  bprod (Bad f i) (Bad g j) = Bad (Pair f g) (i + j)

In fact we can turn any Applicative instance into ProductB instance (admittedly quite useless):

newtype BAp m a f = BAp (m (f a))

instance Functor m => FunctorB (BAp m a) where
  bmap f (BAp b) = BAp (fmap f b)

instance Applicative m => ProductB (BAp m a) where
  buniq x = BAp (pure x)
  bprod (BAp f) (BAp g) = BAp $ liftA2 Pair f g

I didn't checked how applicative laws translate for ProductB but I don't expect any problems

Expand the Wear type family a bit

Greetings!

question

Is there any chance to expand the type family like this:

type family Wear t f a where
  Wear Bare    f (First a) = a
  Wear Bare    f (Last a) = a
  Wear Bare    f a = a
  Wear Covered f a = f a
  Wear (Param _ t) f a = Wear t f a
  Wear t       _ _ = TypeError (     'Text "`Wear` should only be used with "
                               ':<>: 'Text "`Bare` or `Covered`."
                               ':$$: 'Text "`" ':<>: 'ShowType t ':<>: 'Text "`"
                               ':<>: 'Text " is not allowed in this context."
                               )

(This also requires two new instances, of course:

instance GBare n (Rec (P n Identity (Last a)) (Identity (Last a))) (Rec a a) where
  gstrip _ = coerce
  {-# INLINE gstrip #-}

  gcover _ = coerce
  {-# INLINE gcover #-}

)

motivation

use-case is that for combining configs I have f ~ Option, and together with a ~ Last X this gives nice behaviour for generic Semigroup instances. So you'd have a

MyConfig t f = MyConfig
  { a :: Wear t f (Last Bool)
  , b :: Wear t f [Text]
  }

config1 :: MyConfig Covered Option
config2 :: MyConfig Covered Option
staticConfig :: MyConfig Covered Identity
fromOptionIdentity :: Identity a -> Option a -> Identity a
fromOptionIdentity x y = coerce (fromMaybe (coerce x) (coerce y)
finalConfig = bstrip (bzipWith fromOptionIdentity staticConfig (config1 <> config2))
-- (~~see the next issue for what bZipWith is supposed to be~~
--  nevermind, there is a bzipWith already)
-- and have
a finalConfig :: Bool
b finalConfig :: [Text]
-- instead of
a finalConfig :: Last Bool -- pesky left-over from the config merging logic

discussion

We cannot abstract over the type family (yet) so putting this in here is the only way to get this feature, I fear. The downside is that this is a bit confusing behaviour "why does this remove my First/Last wrapper" but then I don't think there are any users who have barbies over First/Last values, apart from this usecase.. it is a bit of a hack.

ghc bug: "Iface type variable out of scope"

Hi! We encountered a ghc regression in our codebase, and when I minimized the code in order to open a ghc ticket, I was left with a short piece of code which uses the barbies-2.0.4.0 library. I am thus creating this ticket in order to help others who might have encountered this strange error message:

typecheckIface
Declaration for $fConstraintsBTYPETypeB
Class ops for dfun $fConstraintsBTYPETypeB:
  Iface type variable out of scope:  k

<no location info>: error:
    Cannot continue after interface file error

Here is a repo which demonstrate how to trigger the bug: https://github.com/gelisam/ghc-9.4-bug/tree/with-barbies

Nested or recursive barbies?

It seems that the AllB and AllBF constraints are insufficient for type hierarchies which nest or recur barbies, threading through the clothing parameter. I made a gist with a minimal test case, and included the type errors as reported by GHCI 8.6.4:

https://gist.github.com/eamsden/2d6c89f899b84b3ad9d5b24d15995fac

This would be very useful e.g. for compilers with rich ASTs, using the clothing types to make fields optional or label fields with source locations and the text they were parsed from, but I can't get it to work.

Couple things that might be useful to have

Hello there. I was wondering if you'd be amenable to adding the following classes (and ways of deriving them):

type Lens' s a = Lens' { project :: forall f. Functor f => (a -> f a) -> s -> f s }

type ProductLike :: (k -> Type) -> Type
class ProductLike t
  where
  lenses :: t (Lens' (t Identity))

and:

type BTraversable1 :: (k -> Type) -> Type
class BTraversable t => BTraversable1 t
  where
  btraverse1 :: Apply e => (forall a. f a -> e (g a)) -> b f -> e (b g) 

There might be some way to turn ProductLike in a sensible way (perhaps involving Co) but based on my rushed thinking so far I haven't been able to think of anything.

Examples of multiple constraints?

I've noticed in the code that the convenience functions like bmapC seem to let you specify a constraint via type applications like bmapC @Show but I haven't found any examples of specifying multiple constraints such as Show and Eq. I haven't really found a good solution for this because type applications doesn't seem to like specifying multiple constraints with one type application. Combining multiple constraints into a type alias with ConstraintKinds doesn't seem to work easier because they don't get partially applied in the same way that individual constraints do. I'm very hesitant to define an entirely new class just for a specific combination of 2 or more classes. Am I missing a better way to do this?

Does not compile with ghc 8.8.1

Trying to compile using ghc-8.8.1 gives the following compilation errors:

Preprocessing library for barbies-1.1.2.1..
Building library for barbies-1.1.2.1..
[ 1 of 17] Compiling Data.Barbie.Internal.Dicts
[ 2 of 17] Compiling Data.Barbie.Internal.Wear
[ 3 of 17] Compiling Data.Functor.Prod
[ 4 of 17] Compiling Data.Generics.GenericN
[ 5 of 17] Compiling Data.Barbie.Internal.Functor
[ 6 of 17] Compiling Data.Barbie.Internal.Traversable
[ 7 of 17] Compiling Data.Barbie.Internal.Product
                     
/home/alex/projects/forks/barbies/src/Data/Barbie/Internal/Product.hs:163:19: error:
    • Expecting one more argument to ‘f’
      Expected a type, but ‘f’ has kind ‘k1 -> *’
    • In the type ‘f’
      In the second argument of ‘($)’, namely
        ‘gbprod @f @g (fromN l) (fromN r)’
      In the expression: toN $ gbprod @f @g (fromN l) (fromN r)
    • Relevant bindings include
        r :: b g (bound at src/Data/Barbie/Internal/Product.hs:162:17)
        l :: b f (bound at src/Data/Barbie/Internal/Product.hs:162:15)
        gbprodDefault :: b f -> b g -> b (Product f g)
          (bound at src/Data/Barbie/Internal/Product.hs:162:1)
    |                
163 |   = toN $ gbprod @f @g (fromN l) (fromN r)
    |                   ^
                     
/home/alex/projects/forks/barbies/src/Data/Barbie/Internal/Product.hs:168:18: error:
    • Expecting one more argument to ‘f’
      Expected a type, but ‘f’ has kind ‘k -> *’
    • In the type ‘f’
      In the first argument of ‘toN’, namely
        ‘(gbuniq @f @f @_ @(RepN (b f)) @(RepN (b (f `Product` f))) x)’
      In the expression:
        toN (gbuniq @f @f @_ @(RepN (b f)) @(RepN (b (f `Product` f))) x)
    • Relevant bindings include
        x :: forall (a :: k). f a
          (bound at src/Data/Barbie/Internal/Product.hs:167:15)
        gbuniqDefault :: (forall (a :: k). f a) -> b f
          (bound at src/Data/Barbie/Internal/Product.hs:167:1)
    |                
168 |   = toN (gbuniq @f @f @_ @(RepN (b f)) @(RepN (b (f `Product` f))) x)
    |                  ^
                     
/home/alex/projects/forks/barbies/src/Data/Barbie/Internal/Product.hs:183:38: error:
    • Expecting one more argument to ‘f’
      Expected a type, but ‘f’ has kind ‘k3 -> *’
    • In the type ‘f’
      In the first argument of ‘M1’, namely ‘(gbprod @f @g l r)’
      In the expression: M1 (gbprod @f @g l r)
    |                
183 |   gbprod (M1 l) (M1 r) = M1 (gbprod @f @g l r)
    |                                      ^
                     
/home/alex/projects/forks/barbies/src/Data/Barbie/Internal/Product.hs:210:23: error:
    • Expecting one more argument to ‘f’
      Expected a type, but ‘f’ has kind ‘k2 -> *’
    • In the type ‘f’
      In the first argument of ‘(:*:)’, namely
        ‘gbuniq @f @g @lf @lg @lfg x’
      In the expression:
        (gbuniq @f @g @lf @lg @lfg x :*: gbuniq @f @g @rf @rg @rfg x)
    • Relevant bindings include
        x :: forall (a :: k2). f a
          (bound at src/Data/Barbie/Internal/Product.hs:210:10)
        gbuniq :: (forall (a :: k2). f a) -> (:*:) lf rf x
          (bound at src/Data/Barbie/Internal/Product.hs:210:3)
    |                
210 |   gbuniq x = (gbuniq @f @g @lf @lg @lfg x :*: gbuniq @f @g @rf @rg @rfg x)
    |                       ^

This is probably related to the way ghc >= 8.8 handles MonoLocalBinds, which is enabled implicitly when TypeFamilies is enabled. This blog post describes this in more detail.

To reproduce this issue, change resolver to nightly-2019-10-18 in stack.yaml and run stack build (I can also provide a minimal nix derivation if it makes things easier)

Pushing applicative effects in?

It seems pulling effects out in Barbies is a trivial matter of btraverse and related functions. But how do I do the opposite, and push effects inwards?

My full question is at https://stackoverflow.com/questions/60900998/pushing-applicative-effects-into-hkd-fields-with-barbies-or-higgledy but basically I am looking for the dual of Higgledy's construct function; Higgledy has these:

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

What I'm looking for is:

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

or, in Barbies parlance,

bdistribute :: (ApplicativeB b, Applicative f, _) => f (b g) -> b (Compose f g)

(yes, I am leaving the door open that bdistribute and nstruct might have more constraints)

Add `Co` constructor for turning products into sums and vice versa

Hi there. Not sure if I was looking carefully enough, but I wasn't able to find an equivalent for this type constructor that is sometimes useful:

newtype Flip t a b = Flip { unFlip :: t b a }

newtype Co b = Co { runCo :: forall r. b (Flip (->) r) -> r }

Given a sum type, like this:

data Something f = TheInt (f Int) | TheString (f String)

You get a record:

getTheInt :: Co Something -> Int
getTheInt (Co f) = f $ TheInt $ Flip id

getTheString :: Co Something -> String
getTheString (Co f) = f $ TheString $ Flip id

buildSomething :: (x -> Int) -> (x -> String) -> x -> Co Something
buildSomething f g x = Co $ \case
  TheInt (Flip fi) -> fi $ f x
  TheString (Flip fs) -> fs $ g x

And given a record type, like so:

data SomethingElse f = SomethingElse { theInt :: f Int, theString :: f String }

You get a sum:

buildAnInt :: Int -> Co SomethingElse
buildAnInt i = Co $ \SomethingElse { theInt } -> unFlip theInt i

buildAString :: String -> Co SomethingElse
buildAString s = Co $ \SomethingElse { theString } -> unFlip theString s

matchSomethingElse :: (Int -> r) -> (String -> r) -> Co SomethingElse -> r
matchSomethingElse f g (Co c) = c $ SomethingElse
  { theInt = Flip f
  , theString = Flip g
  }

Assuming the idea is clear enough, it's more aesthetically pleasing to "keep things closed" by parametrizing Co further, so that it produces something of the same kind as what it consumes.

type Co :: ((k -> *) -> *) -> (k -> *) -> *
newtype Co b f = Co { runCo :: forall r. b (Compose (Flip (->) r) f) -> r }

data Something f = TheInt (f Int) | TheString (f String)

getTheInt :: Co Something f -> f Int
getTheInt (Co f) = f $ TheInt $ Compose $ Flip id

getTheString :: Co Something f -> f String
getTheString (Co f) = f $ TheString $ Compose $ Flip id

buildSomething :: (x -> f Int) -> (x -> f String) -> x -> Co Something f
buildSomething f g x = Co $ \case
  TheInt (Compose (Flip fi)) -> fi $ f x
  TheString (Compose (Flip fs)) -> fs $ g x

data SomethingElse f = SomethingElse { theInt :: f Int, theString :: f String }

buildAnInt :: f Int -> Co SomethingElse f
buildAnInt i = Co $ \SomethingElse { theInt = Compose (Flip x) } -> x i

buildAString :: f String -> Co SomethingElse f
buildAString s = Co $ \SomethingElse { theString = Compose (Flip x) } -> x s

matchSomethingElse :: (f Int -> r) -> (f String -> r) -> Co SomethingElse f -> r
matchSomethingElse f g (Co c) = c $ SomethingElse
  { theInt = Compose $ Flip f
  , theString = Compose $ Flip g
  }

For any product/coproduct type (and I speculate generally for any limit/colimit type), this should form a nice involution:

fwd :: Co (Co Something) f -> Something f
fwd (Co c) = c $ Co $ \case
  TheInt (Compose (Flip fi)) -> fi $ Compose $ Flip TheInt
  TheString (Compose (Flip fs)) -> fs $ Compose $ Flip TheString

bwd :: Something f -> Co (Co Something) f
bwd = \case
  TheInt fi -> Co $ \(Co c) -> c $ TheInt $ Compose $ Flip $ \(Compose (Flip f)) -> f fi
  TheString fs -> Co $ \(Co c) -> c $ TheString $ Compose $ Flip $ \(Compose (Flip f)) -> f fs

We need some kind of typeclass to represent limits/colimits before we can write this more polymorphically, but I'm fairly confident that it works for all products/coproducts at least.

Poly kinded barbie types

Barbie types are currently restricted to kind (Type -> Type) -> Type.

As far as I can tell, all of the type class abstractions in this package would still work if one extends this restriction to a more poly-kinded one: (k -> Type) -> Type.

Thoughts?

ConstraintsT applied to type parameter?

I have a simple case that I can't seem to define (or derive) a ConstraintsT instance for:

data Foo f a = Foo (f a) deriving (Generic)

instance FunctorT Foo

The problem is the f a. The example in the docs (thank you for even having examples!) using T at https://hackage.haskell.org/package/barbies-2.0.1.0/docs/Data-Functor-Transformer.html#g:8 doesn't use a at all. I can't see how you could possibly specify a c a constraint in the AllT type.

Currently we have a bunch of in-house type classes that are similar to what's offered by barbies, and we're trying to get rid of them. Our in-house approach works, but it doesn't separate the constraints from the operations. E.g., we have something like

class FunctorTC (c :: Type -> Constraint) h where
  tmapC :: c b => (forall a. c a => f a -> g a) -> h f b -> h g b

The environment transformer, data EnvT e w a = EnvT e (w a) is another example, and we have a few other internal types that also follow the same pattern.

I've managed a workaround by adding a type parameter to AddT and having taddDicts pass x for that, allowing you to optionally specify c x when you define AddT for your instance:

class FunctorT t => ConstraintsT (t :: (kl -> *) -> kr -> *) where
  type AllT (c :: k -> Constraint) t (x :: kr) :: Constraint
  taddDicts :: forall c f x . AllT c t x => t f x -> t (Dict c `Product` f) x

instance ConstraintsT Foo where
  type AllT c Foo x = c x
  taddDicts (Foo fa) = Foo (Pair Dict fa)

However, that doesn't fully solve things in my case. The next step is a GADT, like

data Goo :: (Type -> Type) -> Type -> Type where
  Cmp :: Ord a => f a -> f a -> Goo f Bool

where it can't find an instance for c a, and I don't even know where to begin on this one.

Support for capability-style records

I really don't know if this is possible, but I'm referring to capability records like:

data LogCap (m :: Type -> Type) 
  = LogCap 
  { logInfo :: String -> String -> m ()
  , logError :: String -> String -> m () 
  } deriving Generic

From the outside these records look like HKDs - every field contains the type variable, but you might consider function parameters being "hidden" from it (one of the disclaimers from ProductB).

The first problem to solve is the FunctorB instance, as GFunctorB doesn't cut it:

• No instance for (barbies-1.1.3.0:Data.Barbie.Internal.Functor.GFunctorB
                         *
                         *
                         f
                         g
                         (Rec
                            *
                            (String
                             -> String
                             -> barbies-1.1.3.0:Data.Generics.GenericN.Param * * 0 f ())
                            (String -> String -> f ()))
                         (Rec
                            *
                            (String
                             -> String
                             -> barbies-1.1.3.0:Data.Generics.GenericN.Param * * 0 g ())
                            (String -> String -> g ())))

Intuitively though, it seems that we should be able to traverse an arrow with (f a -> g a), drilling deeper by creating lambdas until we come to an m a.

The point of all this is that at the end of the day it would be nice to be able to write a generic lift function for any of these forms of HKDT.

Given:

newtype Apply (f :: Type -> Type) (g :: Type -> Type) a
  = Apply { runApply :: f a -> g a }


bapply
  :: ProductB b
  => b (Apply i o)
  -> b i
  -> b o
bapply =
  bzipWith runApply

You could easily do something like:

let 
  ioLogCap :: LogCap IO 
  ioLogCap = ioLoggingCapabilities 

  resourceTLogCap :: LogCap (ResourceT IO) 
  resourceTLogCap = bapply (buniq $ Apply lift) ioLogCap
in 
  runResourceT $ f resourceTLogCap

which could kill a pleasant amount of boilerplate when using capability records.

Thoughts? I might be crazy ¯_(ツ)_/¯

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.