ekmett / bifunctors Goto Github PK
View Code? Open in Web Editor NEWHaskell 98 bifunctors, bifoldables and bitraversables
License: Other
Haskell 98 bifunctors, bifoldables and bitraversables
License: Other
Would it be useful to include derivation templates for bifunctors, bifoldables and bitraversables? I benefit quite a lot from the language extensions DeriveFunctor, DeriveFoldable, DeriveTraversable, and having templates that derives their binary counterparts would remove a lot of boilerplate in code I currently develop. I'd guess I'm not the only one experiencing this, so I was wondering if this seemed reasonable to include this in the library itself.
For example something à la
data Foo a b = L a
| R b
| LR a b
| None
$(derive [makeBifunctor, makeBifoldable, makeBitraversable] ''Foo)
I found myself needing to compose a bifunctor and a functor in a way that preserved bifunctorality, and I think that this code might be generally useful. I'm not really sold on the naming but I hope it's clear enough, especially with the type signatures; if there's a better name for this sort of thing that comes from theory or just intuition, please feel free to change it.
-- | Compose a bifunctor and two functors, one on the 'outside' (left) of the bifunctor and one on the 'inside' (right), preserving bifunctorality.
newtype HalfBicompose f p g a b = HalfBicompose { getHalfBicompose :: f (p a (g b)) }
-- | A bifunctor composed with a functor, preserving bifunctorality.
type HalfBicomposeL = HalfBicompose Identity
-- | A functor composed with a bifunctor, preserving bifunctorality.
type HalfBicomposeR f p = HalfBicompose f p Identity
instance (Bifunctor p, Functor f, Functor g) => Bifunctor (HalfBicompose f p g) where
bimap f g = HalfBicompose . fmap (bimap f (fmap g)) . getHalfBicompose
instance (Bifunctor p, Functor f, Functor g) => Functor (HalfBicompose f p g a) where
fmap = second
instance (Biapply p, Apply f, Apply g) => Biapply (HalfBicompose f p g) where
HalfBicompose f <<.>> HalfBicompose a = HalfBicompose $ liftF2 (bilift2 ($) (<.>)) f a
instance (Biapplicative p, Applicative f, Applicative g) => Biapplicative (HalfBicompose f p g) where
bipure a b = HalfBicompose $ pure (bipure a (pure b))
HalfBicompose f <<*>> HalfBicompose a = HalfBicompose $ liftA2 (biliftA2 ($) (<*>)) f a
instance (Bifoldable p, Foldable f, Foldable g) => Bifoldable (HalfBicompose g p f) where
bifoldMap f g = foldMap (bifoldMap f (foldMap g)) . getHalfBicompose
instance (Bifoldable p, Foldable f, Foldable g) => Foldable (HalfBicompose f p g a) where
foldMap = bifoldMap (const mempty)
instance (Bitraversable p, Traversable f, Traversable g) => Bitraversable (HalfBicompose f p g) where
bitraverse f g = fmap HalfBicompose . traverse (bitraverse f (traverse g)) . getHalfBicompose
instance (Bitraversable p, Traversable f, Traversable g) => Traversable (HalfBicompose f p g a) where
traverse = bitraverse pure
fromHalfBicomposeL :: HalfBicomposeL p f a b -> p a (f b)
fromHalfBicomposeL = runIdentity . getHalfBicompose
toHalfBicomposeL :: p a (f b) -> HalfBicomposeL p f a b
toHalfBicomposeL = HalfBicompose . Identity
fromHalfBicomposeR :: (Functor f, Bifunctor p) => HalfBicomposeR f p a b -> f (p a b)
fromHalfBicomposeR = fmap (second runIdentity) . getHalfBicompose
toHalfBicomposeR :: (Functor f, Bifunctor p) => f (p a b) -> HalfBicomposeR f p a b
toHalfBicomposeR = HalfBicompose . fmap (second Identity)
halfBicomposeL2R :: (Applicative f, Bifunctor p, Traversable (p a)) => HalfBicomposeL p f a b -> HalfBicomposeR f p a b
halfBicomposeL2R = toHalfBicomposeR . sequenceA . fromHalfBicomposeL
halfBicomposeR2L :: (Traversable f, Bifunctor p, Applicative (p a)) => HalfBicomposeR f p a b -> HalfBicomposeL p f a b
halfBicomposeR2L = toHalfBicomposeL . sequenceA . fromHalfBicomposeR
is it worth backporting?
Section 3 of Constructing Applicative Functors describes defining ZipList
as a fixed point of the composition of Maybe ∘ ×
. If we have a composition of a functor and a bifunctor
newtype (f · g) a b = C (f (g a b))
we can define ZipList
as Fix (Maybe · (,))
type ZipList = Fix (Maybe · (,))
pattern Nil :: ZipList a
pattern Nil = In (C Nothing)
infixr 5 :::
pattern (:::) :: a -> ZipList a -> ZipList a
pattern a:::as = In (C (Just (as, a)))
Are any of these (/ inverses) useful
toSum :: Biff Either f g a a -> Sum f g a
toSum (Biff (Left fa)) = InL fa
toSum (Biff (Right ga)) = InR ga
toProduct :: Biff (,) f g a a -> Product f g a
toProduct (Biff (fa, ga)) = Pair fa ga
-- transformers
toLift :: Biff Either Identity g a a -> Lift g a
toLift (Biff (Left (Identity a))) = Pure a
toLift (Biff (Right ga)) = Other ga
Adding BifunctorComonad
carries a price. We don't have a good place to put the BifunctorComonad
instance for Tannen
it'd have to live as an orphan in comonad
or cause us to pick up a comonad
dependency.
On the other hand we have a Cayley
construction in profunctors
, which does exactly the same thing and works just fine because profunctors
has a comonad
dependency.
We could add a comonad
dependency to this package, which might cause some hue and cry over the fact that Data.Bifunctor
moved into base.
Or we could try to consolidate more of these little packages into a bigger package and kill the issue with cycles, and avoid orphan instances all in one go.
The issue is that in the profunctors
world the free/cofree versions of each construction really should live with the class in question as they are always available, while here, we have a bit more freedom.
The order of Fix
is unfortunate, it means that given a standard base functor
data FList a list = FNil | FCons a list
instance Bifunctor FList where
bimap :: (a -> a') -> (list -> list') -> (FList a list -> FList a' list')
bimap f g = \case
FNil -> FNil
FCons a b -> FCons (f a) (g b)
instance Bifoldable FList where
bifoldMap :: Monoid m => (a -> m) -> (list -> m) -> (FList a list -> m)
bifoldMap f g = \case
FNil -> mempty
FCons a b -> f a <> g b
we get an unexpected order of elements
type List = Fix FList
pattern Nil :: List a
pattern Nil = In FNil
infixr 5 :::
pattern (:::) :: a -> List a -> List a
pattern a:::as = In (FCons as a)
>>> toList (1:::2:::3:::4:::Nil)
[4,3,2,1]
I had the same problems with defining newtype ZipList a = ZL (Fix (Tannen Maybe (,))
from #57 where I need to wrap it in Reverse
to get the Foldable
I want.
if it were the other way round like The Essence of the Iterator Pattern
data Fix' p a = In' { out' :: p a (Fix' p a) }
we would get the right ordering and the argument of fold
fits the shape of an algebra
type Alg f a = f a -> a
fold :: Bifunctor f => Alg (f a) b -> (Fix' f a -> b)
fold f = f . second . (fold f) . out'
foldMapLefts :: (Bifoldable f, Monoid m)
=> (a -> m) -> f a b -> m
foldMapLefts f = bifoldMap f (const mempty)
Etc., etc.
I can currently write the following:
data Hello x a = Hello { foo :: Maybe [a] }
deriveBifunctor ''Hello
But I cannot write the following equivalent definition:
data Hello x a = Hello { foo :: Compose Maybe [] a }
deriveBifunctor ''Helo
It is rejected with:
src/Language/Haskell/GHC/Token.hs:18:1: error:
• Couldn't match kind ‘* -> *’ with ‘*’
When matching types
p0 :: * -> * -> *
Compose Maybe :: (* -> *) -> * -> *
Expected type: p0 b0 c
Actual type: Compose Maybe [] c
• In the third argument of ‘Data.Bifunctor.bimap’, namely
‘_arg1_aT2x’
In the first argument of ‘Hello’, namely
‘(((Data.Bifunctor.bimap (\ x_aT2D -> x_aT2D)) g_aT2o) _arg1_aT2x)’
In the expression:
Hello
(((Data.Bifunctor.bimap (\ x_aT2D -> x_aT2D)) g_aT2o) _arg1_aT2x)
Could this be made to work?
With
newtype XY a b= XY { getResp :: Either X (Y a b) }
deriving (Show, Functor, Foldable)
we get
TH.deriveBifoldable ''XY
======>
instance Bifoldable XY where
bifoldr
= \ f_asLl g_asLm z_asLn value_asLo
-> ((((bifunctors-5.5.9:Data.Bifunctor.TH.Internal.bifoldrConst
(case value_asLo of {
XY _arg1_asMt
-> ((\ n1_asMn n2_asMo
-> (((bifoldr (\ n1_asMp n2_asMq -> n2_asMq))
(\ n1_asMr n2_asMs
-> (((bifoldr f_asLl) g_asLm) n2_asMs) n1_asMr))
n2_asMo)
n1_asMn)
_arg1_asMt)
z_asLn }))
f_asLl)
g_asLm)
z_asLn)
value_asLo
bifoldMap
= \ f_asMv g_asMw value_asMy
-> (((bifunctors-5.5.9:Data.Bifunctor.TH.Internal.bifoldMapConst
(case value_asMy of {
XY _arg1_asNy
-> ((bifoldMap (\ n_asNx -> mempty)) ((bifoldMap f_asMv) g_asMw))
_arg1_asNy }))
f_asMv)
g_asMw)
value_asMy
and two warnings:
warning: [-Wunused-matches]
Defined but not used: ‘n1’
|
304 | TH.deriveBifoldable ''XY
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: [-Wunused-matches]
Defined but not used: ‘n’
|
304 | TH.deriveBifoldable ''XY
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The offenses are (\ n1_asMp n2_asMq -> n2_asMq)
and (\ n_asNx -> mempty)
, I think.
We're unfortunately handling this by disabling that warning in the module, for now.
Thanks!
Analogous to Control.Applicative.Lift
data Lift f a = Pure a | Other (f a)
we can define
data Bilift f a b = Bipure a b | Biother (f a b)
It's easy to have an install plan with old bifunctors
and (new) bifunctor-classes-compat
thus having two Bifunctor
definitions lying around for GHC-8.0 and older.
I don't recall how we have dealt with such issues previously, or did we at all?
EDIT: real world example:
Imagine I change assoc
to depend on bifunctor-classes-compat
: every current user of it (e.g. these
) will break, as these
would define Bifunctor These
for bifunctors.Bifunctor
.
Does this mean, that to do that change in assoc
, I should make a major version bump, so the downstream would need to react? I guess so, unless there's some trick which escapes me now.
instance Category cat => Semigroup (Join cat a) where
Join f <> Join g = Join (f . g)
instance Category cat => Monoid (Join cat a) where
mempty = Join id
mappend = (<>)
This allows deriving Monoid (Kleisli m a a)
and
newtype Endo a = Endo { appEndo :: Join (->) a }
deriving
(Semigroup, Monoid)
src/Data/Bifunctor/TH.hs:291:18: parse error on input `->'
I often use something like this:
bothmap :: Bifunctor f => (a -> b) -> f a a -> f b b
bothmap f = bimap f f
I typically use it with pairs where function f
is a lambda expression and I'm lazy to bind it to a name. It would be nice to have it in the library.
@phadej discovered this via ekmett/pointed#17 (comment)
There's two options from here on:
bifunctors-5.4.0.1
release which fixes this by marking the affected modules trustworthy and restore their previous SafeHaskell status (and then declare bifunctors-5.4
a broken release)or the more messy
bifunctors < 5.4
upper bounds in some places.ghc-8.0.0.20160109 says:
[ 1 of 15] Compiling Paths_bifunctors ( dist/build/autogen/Paths_bifunctors.hs, dist/build/Paths_bifunctors.o )
[ 2 of 15] Compiling Data.Bifunctor.TH.Internal ( src/Data/Bifunctor/TH/Internal.hs, dist/build/Data/Bifunctor/TH/Internal.o )
src/Data/Bifunctor/TH/Internal.hs:221:19: error:
Not in scope: data constructor ‘FamilyD’
Perhaps you meant ‘FamilyI’ (imported from Language.Haskell.TH.Syntax)
In the RHS of the composition law stated in Data.Bitraversable: that should be bitraverse
, not traverse
, right?
Data.Bifunctor.Flip's description duplicates Data.Bifunctor.Clown's; I -think- that's wrong, but if I squint, the phrasing makes cryptic sense for both of them. I'd probably change Flip to "Swap the arguments to a Bifunctor".
Does this belong anywhere? Just as we have Cayley
newtype Cayley f p a b = Cayley (f (p a b))
instance (Applicative f, Category p) => Category (Cayley f p) where
we can have
newtype Bicayley f p q a b = Bicayley (f (p a b) (q a b))
instance (Biapplicative f, Category p, Category q) => Category (Bicayley f p q) where
id = Bicayley (bipure id id)
Bicayley f . Bicayley g = Bicayley (biliftA2 (.) (.) f g)
No use cases personally.
In the spirit of "If we can, then we should". I think most of them can just be derived. Yoneda
and Coyoneda
need to be written by hand, but it's no big deal. I'm pretty sure Day
is a no-go.
instance (Biapplicative p, Monoid a) => Applicative (WrappedBifunctor p a) where
pure a = WrapBifunctor (bipure mempty a)
{-# inline pure #-}
liftA2 f (WrapBifunctor xs) (WrapBifunctor ys) = WrapBifunctor $ biliftA2 mappend f xs ys
{-# inline liftA2 #-}
It turns out that Data.Bifunctor.TH
suffers from the same bug that caused GHC#17880. Here is a minimal demonstration of the bug:
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TemplateHaskell #-}
module Bug where
import Data.Bifunctor.TH
data T a b = MkT (forall c. c -> (forall d. a -> d) -> a)
$(deriveBifunctor ''T)
This should compile, but fails with the following error:
Bug.hs:8:3: error:
• Couldn't match type ‘forall d1. a -> d1’ with ‘a -> p0’
Expected type: c1 -> (a -> p0) -> a
Actual type: c1 -> (forall d. a -> d) -> a
• In the first argument of ‘\ x_ajqV b_ajqW
-> (\ x_ajqX b_ajqY
-> f_ajqR
(x_ajqX
((\ x_ajqZ b_ajr0
-> (\ x_ajr1 -> x_ajr1)
(x_ajqZ (f_ajqR b_ajr0)))
b_ajqY)))
(x_ajqV ((\ x_ajr2 -> x_ajr2) b_ajqW))’, namely
‘_arg1_ajqU’
In the first argument of ‘MkT’, namely
‘((\ x_ajqV b_ajqW
-> (\ x_ajqX b_ajqY
-> f_ajqR
(x_ajqX
((\ x_ajqZ b_ajr0 -> (\ x_ajr1 -> x_ajr1) (x_ajqZ (f_ajqR b_ajr0)))
b_ajqY)))
(x_ajqV ((\ x_ajr2 -> x_ajr2) b_ajqW)))
_arg1_ajqU)’
In the expression:
MkT
((\ x_ajqV b_ajqW
-> (\ x_ajqX b_ajqY
-> f_ajqR
(x_ajqX
((\ x_ajqZ b_ajr0 -> (\ x_ajr1 -> x_ajr1) (x_ajqZ (f_ajqR b_ajr0)))
b_ajqY)))
(x_ajqV ((\ x_ajr2 -> x_ajr2) b_ajqW)))
_arg1_ajqU)
• Relevant bindings include
_arg1_ajqU :: forall c. c -> (forall d. a -> d) -> a
(bound at Bug.hs:8:3)
value_ajqT :: T a c (bound at Bug.hs:8:3)
f_ajqR :: a -> b (bound at Bug.hs:8:3)
bimap :: (a -> b) -> (c -> d) -> T a c -> T b d
(bound at Bug.hs:8:3)
|
8 | $(deriveBifunctor ''T)
| ^^^^^^^^^^^^^^^^^^^
GHC has fixed this bug upstream, and it should be possible to apply a similar fix in Data.Bifunctor.TH
.
I think it'd be useful to have the possibility of viewing a Bifunctor
as a standard Functor
in each its type argument. I suggest something like:
-- | Views a bifunctor as a functor in its first type argument.
newtype AsFunctor1 p b a = AsFunctor1 { getAsFunctor1 :: p a b }
instance Bifunctor p => Functor (AsFunctor1 p b) where
fmap f = AsFunctor1 . first f . getAsFunctor1
{-# INLINE fmap #-}
-- | Views a bifunctor as a functor in its second type argument.
newtype AsFunctor2 p a b = AsFunctor2 { getAsFunctor2 :: p a b }
instance Bifunctor p => Functor (AsFunctor2 p a) where
fmap f = AsFunctor2 . second f . getAsFunctor2
{-# INLINE fmap #-}
Consider the following setup:
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
module Bug where
import Data.Bifunctor
import Data.Bifunctor.TH
import Data.Kind
type family F :: Type -> Type -> Type
type instance F = Either
newtype T a b = MkT (F a b)
One can derive a Bifunctor
instance for T
using GeneralizedNewtypeDeriving
:
deriving newtype instance Bifunctor T
Trying to use Template Haskell to accomplish the same thing, however, fails:
$(deriveBifunctor ''T)
Bug.hs:1:1: error:
Exception when trying to run compile-time code:
Constructor ‘MkT‘ must only use its last two type variable(s) within the last two argument(s) of a data type
CallStack (from HasCallStack):
error, called at src/Data/Bifunctor/TH.hs:903:32 in bifunctors-5.5.7-382d87a82a5fe85a191a6a3943a4d9c453af2598fed3436d948a78b02f584473:Data.Bifunctor.TH
Code: deriveBifunctor ''T
|
1 | {-# LANGUAGE DerivingStrategies #-}
| ^
The issue is that deriveBifunctor
is too conservative: it bails out if the last type variables (a
/b
) occur as an argument to any type family whatsoever. Although F
is a type family, its use in MkT
is benign, since the type F a b
oversaturates F
(it has zero type variable binders). deriveBifunctor
should take the number of type variable binders a type family has into account so as to permit the program above.
Is there a reason why Bifunctor
, Bifoldable
and Bitraversable
aren't subclasses of Functor
, Foldable
and Traversable
? fmap
and right
should be the same by parametricity, and I would personally be very surprised to see bifoldMap (const mempty)
and foldMap
or bitraverse pure
and traverse
produce different results.
src/Data/Bifunctor/TH.hs:675:5:
parse error on input ‘-- * after substituting * for the dropped kind variables. If not, throw an error.’
(Data.Monoid
exports <>
starting with base-4.5.0.0
)
[11 of 12] Compiling Data.Bifunctor.Product ( src/Data/Bifunctor/Product.hs, dist/dist-sandbox-43547874/build/Data/Bifunctor/Product.o )
src/Data/Bifunctor/Product.hs:22:37:
Module `Data.Monoid' does not export `(<>)'
Failed to install bifunctors-4.1.0.1
assoc
provides Bifunctor p => Swap p
and Assoc p
type-classes.
Currently assoc
depends on bifunctors
to provide instances for Flip
, Biff
etc types defined in bifunctors
.
But now we have a light compatibility package bifunctor-classes-compat
.
So I propose a following dependency rearrangement:
assoc
depends on bifunctor-classes-compat
and provides type-classes and instances for things in base
bifunctors
depends on assoc
to provide Swap
and Assoc
instances for extra bifunctors defined herelens
is not really affected, as it still depends on both assoc
and bifunctors
. lens
defines Wrapped
instance for Biff
, Clown
... etc, otherwise it could drop (at least direct) bifunctors
dependency as well)these
could drop bifunctors
dependency as it only needs Bifunctor
type-class and type-classes defined in assoc
package.This will be breaking change in assoc
but only a minor change in bifunctors
.
How does this sound?
Configuring bifunctors-5.3...
Flags chosen: tagged=True, semigroups=True
Dependency QuickCheck -any: using QuickCheck-2.8.2
Dependency base -any: using base-4.9.0.0
Dependency base-orphans -any: using base-orphans-0.5.3
Dependency bifunctors -any: using bifunctors-5.3
Dependency comonad -any: using comonad-5
Dependency containers -any: using containers-0.5.7.1
Dependency hspec -any: using hspec-2.2.3
Dependency semigroups >=0.8.3.1 && <1: using semigroups-0.18.1
Dependency tagged >=0.7.3 && <1: using tagged-0.8.3
Dependency template-haskell -any: using template-haskell-2.11.0.0
Dependency transformers -any: using transformers-0.5.1.0
Dependency transformers-compat -any: using transformers-compat-0.5.1.4
Using Cabal-1.23.1.0 compiled by ghc-8.0
Using compiler: ghc-8.0.0.20160204
[...]
Building bifunctors-5.3...
Preprocessing library bifunctors-5.3...
[ 1 of 17] Compiling Paths_bifunctors ( dist/build/autogen/Paths_bifunctors.hs, dist/build/Paths_bifunctors.o )
[ 2 of 17] Compiling Data.Bifunctor.TH.Internal ( src/Data/Bifunctor/TH/Internal.hs, dist/build/Data/Bifunctor/TH/Internal.o )
[ 3 of 17] Compiling Data.Bifunctor.TH ( src/Data/Bifunctor/TH.hs, dist/build/Data/Bifunctor/TH.o )
[ 4 of 17] Compiling Data.Bifunctor.Functor ( src/Data/Bifunctor/Functor.hs, dist/build/Data/Bifunctor/Functor.o )
[ 5 of 17] Compiling Data.Bifoldable ( src/Data/Bifoldable.hs, dist/build/Data/Bifoldable.o )
[ 6 of 17] Compiling Data.Bitraversable ( src/Data/Bitraversable.hs, dist/build/Data/Bitraversable.o )
src/Data/Bitraversable.hs:212:10: error:
• No instance for (Bifunctor (K1 i))
arising from the superclasses of an instance declaration
• In the instance declaration for ‘Bitraversable (K1 i)’
I recently committed some fixes to another branch in an attempt to fix the CI for bifunctors
. I haven't pushed to master
yet, however, because it does a couple of opinionated things:
master
branch of distributive
, it vendors in the comonad
library using a source-repository-package
in the cabal.project
file. See here.cabal.project
file. bifunctors
used to have one, but @ekmett appears to have removed it in bc5d8e8 for reasons that aren't clear to me.In any case, let me briefly explain why I favor (1) and (2) above:
source-repository-package
allows vendored libraries to be saved in cabal
's store (using cabal-instal-3.4
or later), which means that you don't have to constantly rebuild them. More critically, it also means that vendored libraries can be cached in CI, so it will save quite a bit of CI time in the long run.cabal.project
file makes it so that I can check out the bifunctors
repo within the context of another project with a cabal.project
file and not have the latter cabal.project
file clobber it. This is important for my workflows, as I manage bifunctors
within another repo.@ekmett: I'm raising this issue because I don't want to step on your toes when you're developing your libraries. Let me know your preferences and I can find a way to make both of us happy.
For the change of adding a superclass to Bifunctor
, and later to Bifoldable
and Bitraversable
we need Functor
instances of Data.Bifunctor.Product
and others. I tried it out and it seems enough to do deriving stock Functor
.
I'm writing some code and I've run into the need to have a type like this:
newtype Uncurry f (p :: (k, l)) = Uncurry (forall a b. p ~ '(,) a b => f a b)
I've tried to see if there exists a type like that in any popular packages, but there doesn't seem to be. There may be under a different name though. But assuming there isn't, would you be interested in adding such a type to bifunctors
? It seems like a reasonable place to put such a type. If you're interested, I can do up a pull request with appropriate instances and everything. If you have some guidance about what module you'd like to put it in (i.e., what you'd call that module) that would help too, because I'm not really sure what to call it. Thanks!
Subject says it all
Add Compose f g a b
(= Biff f g dentity a b
) from Edward's monad homomorphisms
type COMPOSE f g = Biff f g Identity
newtype Compose f g a b = COMPOSE { runCOMPOSE :: COMPOSE f g a b }
deriving (Functor, Foldable, Traversable, Bifunctor, Bifoldable, Biapplicative)
It's been a while.
We get many ambiguous imports for Data.Bifunctor when building packages (e.g. free
).
What is the story for this?
Data.Bifunctor
from this package and use it from base
insteadPackageImports
everywhere?With bifunctors one can map over each element, but how about just extracting an element? A generalization of fst and snd (of a tuple). Maybe something like this already exists with lens?
data Bar a b where
Bar :: b -> Bar True b
data Foo b c where
Foo :: Bar x c -> Foo b c
deriveBifunctor ''Foo
• Couldn't match kind ‘Bool’ with ‘*’
When matching types
p0 :: * -> * -> *
Bar :: Bool -> * -> *
Expected: Bar x d
Actual: p0 b0 d
• In the first argument of ‘Foo’, namely
‘(((bimap (\ _n_a7u1C -> _n_a7u1C)) g_a7u1v) _arg1_a7u1B)’
In the expression:
Foo (((bimap (\ _n_a7u1C -> _n_a7u1C)) g_a7u1v) _arg1_a7u1B)
In a case alternative:
Foo _arg1_a7u1B
-> Foo (((bimap (\ _n_a7u1C -> _n_a7u1C)) g_a7u1v) _arg1_a7u1B)
|
419 | deriveBifunctor ''Foo
| ^^^^^^^^^^^^^^^^^^^^^
The derived instance shouldn't be using bimap
here but rather just fmap
Bifunctors have BiApply, but lack BiPointed. like
class BiPointed f where
bipoint :: a -> b -> f a b
There aren't many types in base which can benefit from it (I can't actually imagine anything but (,)), but it might be useful for people using bifunctors package.
The instances of Bifunctor and Biapply aren't as lazy as they could be for single-constructor types. For example, comparing (Control.Arrow.***) and Data.Bifunctor.bimap:
> (Control.Arrow.***) (const 0) (const 1) undefined :: (Int,Int)
(0,1)
> Data.Bifunctor.bimap (const 0) (const 1) undefined :: (Int,Int)
*** Exception: Prelude.undefined
The Bifunctor instances for the various tuple types and Const should be using irrefutable patterns. Likewise for the Biapply (,) instance.
This is a bit of an unusual request, but would it be feasible to remove bifunctors
' dependency on semigroupoids
and move the affected instances to semigroupoids
instead? The reason I want this is because as of GHC 7.10, Data.Bifunctor
is in base
, so anyone who wants to backport Bifunctor
support for older versions of GHC will need to depend on bifunctors
. This brings in a whole host of unneeded dependencies (e.g., contravariant
, StateVar
, distributive
, etc.) just to be able to use Data.Bifunctor
. Reversing the order of the dependencies would lighten the installation burden for users of older versions of GHC.
This originally came out of a discussion in ekmett/semigroupoids#130, which discusses some additional changes to bifunctors
needed for the benefit of a future foldable1-classes-compat
package. In particular, we wish to split out the Data.Bifunctor
, Data.Bifoldable
, and Data.Bitraversable
modules from bifunctors
into a more minimal bifunctor-classes-compat
package with fewer dependencies, and then have bifunctors
re-export these modules. There is one catch: cabal
's reexported-modules
feature only supports GHC 7.10 and later. As a result, we have decided that this is as good of a time as any to drop pre-8.0 support in bifunctors
, which is perhaps overdue as it is.
Note that this really only matters for the 5
branch of bifunctors
. The main
branch only support GHC 8.6 and later, so none of these changes would apply to that branch.
(More context: GHC Trac #10448)
This involves several sub-items:
bifunctors
conditionally hide Data.Bifoldable
and Data.Bitraversable
. (commit) (Hackage)base < 4.10
upper bounds to all bifunctors
releases on Hackage to mark them as incompatible with GHC 8.2's base
.We also need to (conditionally) move instances into the following packages:
From here, I don't know if it requires unsafe
biSequence :: (Biapplicative p, T.Traversable t) => t (p a b) -> p (t a) (t b)
biSequence = bimap (fmap somethingCoerce) (fmap somethingCoerce)
. unCollapse
. T.sequenceA
. fmap (Collapse . bimap Something Something)
It looks like on 391c314#L283 there's a t .
missing on the right hand side of that law.
Hackage says
naturality
bitraverse (t . f) (t . g) ≡ t . bitraverse f g for every applicative transformation t
Preprocessing test suite 'bifunctors-spec' for bifunctors-5.2...
[1 of 2] Compiling BifunctorSpec ( tests/BifunctorSpec.hs, dist/build/bifunctors-spec/bifunctors-spec-tmp/BifunctorSpec.dyn_o )
tests/BifunctorSpec.hs:1:1: error:
Exception when trying to run compile-time code:
src/Data/Bifunctor/TH.hs:(341,1)-(356,70): Non-exhaustive patterns in function makeBiFunForCon
Code: deriveBifoldable ''StrangeGADT
I see there are closed issues about GHC 8, so if this has already been fixed feel free to close this issue and shout at me!
Do they make sense? I only have patience to define Eq1
instance (Biapplicative f, Bifoldable f) => Eq1 (Fix f) where
liftEq :: (a -> a' -> Bool) -> (Fix f a -> Fix f a' -> Bool)
liftEq (===) (In x) (In y) = biand z where
z :: f Bool Bool
z = biliftA2 (liftEq (===)) (===) x y
I forgot if I've brought this up before, should this instance exist?
instance (Applicative f, Biapplicative bi, Monoid a) => Applicative (Tannen f bi a) where
pure :: b -> Tannen f bi a b
pure b = Tannen do
pure (bipure mempty b)
liftA2 :: (b1 -> b2 -> b3) -> (Tannen f bi a b1 -> Tannen f bi a b2 -> Tannen f bi a b3)
liftA2 (·) (Tannen as) (Tannen bs) = Tannen do
liftA2 (biliftA2 (<>) (·)) as bs
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.