bifunctors
bifunctors copied to clipboard
Add Bilift
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)
If #12369
goes through we can define it as a data instance
data family Lift (f :: k) :: k
data instance Lift (f::Type -> Type) (a::Type) = Pure a | Other (f a)
data instance Lift (f::Type -> Type -> Type) (a::Type) (b::Type) = Bipure a b | Biother (f a b)
I suppose Maybe
is an instance of this
data family Maybe (f :: k) :: k
data instance Maybe (a::Type) = Nothing | Just a
data instance Maybe (f::Type -> Type) (a::Type) = Pure a | Other (f a)
hm
This could be given a Biapplicative
instance, similar to original lift. We can use a Biapplicative f
constraint but a Biapply f
constraint suffices.
instance Biapply f => Biapplicative (Bilift f) where
bipure :: a -> b -> Bilift f a b
bipure = Bipure
(<<*>>) :: Bilift f (a -> a') (b -> b') -> (Bilift f a b -> Bilift f a' b')
Bipure f g <<*>> Bipure x y = Bipure (f x) (g y)
Bipure f g <<*>> Biother fxy = Biother (bimap f g fxy)
Biother fga <<*>> Bipure x y = Biother (bimap ($ x) ($ y) fga)
Biother fga <<*>> Biother fxy = Biother (fga <<.>> fxy)
Random thought, we have (??)
(??) :: Functor f => f (a -> b) -> a -> f b
fab ?? a = fmap ($ a) fab
already, which is a generalisation of flip = (??) @((->) _)
. A similar thing can be done for Bifunctor
(???) :: Bifunctor f => a -> b -> f (a -> a') (b -> b') -> f a' b'
(a ??? b) xs = bimap ($ a) ($ b) xs
(???) :: Bifunctor f => a -> p (a -> a1) (a -> a2) -> p a1 a2
a ??? xs = bimap ($ a) ($ a) xs
is it interesting? Almost seems like an inverse of closed
(unclosed?)
closed :: Closed p => p a b -> p (x -> a) (x -> b)
Seems like an Iso
closing
:: (Closed p, Bifunctor p')
=> x'
-> y'
-> Iso (p a b) (p' a' b')
(p (x -> a) (x -> b)) (p' (x' -> a') (y' -> b'))
closing a b = iso closed (bimap ($ a) ($ b))
My first impression is that Bilift
seems like a sensible thing to have. After all, we have Applicative
transformers, so why not Biapplicative
transformers?
If you'd care to whip up a pull request, I'll take a look at it.
How about the dependencies, Biapply
is from semigroupoids which depends on bifunctors
True. I see two options:
- For consistency with
Lift
fromtransformers
, we just make itinstance Biapplicative f => Biapplicative (Bilift f)
. This is a stronger constraint than is necessary, but then again, so are several other instances in theApplicative
diaspora. - We put
Bilift
insemigroupoids
instead so that we can give the instance aBiapply
context instead.
I don't have a strong opinion on which direction to take.
The situation with Lift
(as with Monoid a => Monoid (Maybe a)
) is unfortunate and I worry about digging a deeper hole
It's worth pointing out that semigroupoids has
instance Apply f => Applicative (MaybeApply f)
for the type newtype MaybeApply f a = MaybeApply (Either (f a) a)
so I suppose we can add
-- -semigroupoids- package
import qualified Data.Bifunctor.Sum as Bi
newtype MaybeBiapply f a b = MaybeBiapply (Bi.Sum f (,) a b)
deriving newtype
(Bifunctor, Bifoldable)
instance Biapply f => Biapplicative (MaybeBiapply f)
-- -bifunctors- package
data Bilift f a b = Bipure a b | Biother (f a b)
instance Biapplicative f => Biapplicative (Bilift f)
— this would be a mixture of option 1. and 2. mirroring the situation with Lift
/ MaybeApply