jcpetruzza / barbies Goto Github PK
View Code? Open in Web Editor NEWLicense: BSD 3-Clause "New" or "Revised" License
License: BSD 3-Clause "New" or "Revised" License
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
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
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'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
The following currently doesn't work:
data B b (f :: * -> *) = B (b f)
deriving (Generic)
instance FunctorB b => FunctorB (B b)
Of course, one can write the instances manually...
instance FunctorB b => FunctorB (B b) where
bmap f (B bf) = B (bmap f bf)
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:
I haven't figured out how to make it work with 8.6 without resorting to CPP though.
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.
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
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.
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?
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.
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):
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.
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
Greetings!
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 #-}
)
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
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.
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
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.
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.
Hey, I saw that harg
depends on barbies-2.0.3
; any chance this could get pushed up to Hackage?
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?
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)
It looks like this library has some typeclasses that are also defined in parameterized-utils
. I wonder if we could reduce the duplication of effort.
In particular:
FunctorB <-> Data.Parameterized.TraversableF.FunctorF
TraversableB <-> Data.Parameterized.TraversableF.TraversableF
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)
This library looks super interesting. The README states that barbies are a common Haskell idiom.
What is the usual name for this and where can I find out more?
Also, where can I find example uses?
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.
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?
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.
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 ¯_(ツ)_/¯
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.