bifunctors icon indicating copy to clipboard operation
bifunctors copied to clipboard

Add Bilift

Open Icelandjack opened this issue 7 years ago • 8 comments

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)

Icelandjack avatar Mar 01 '17 16:03 Icelandjack

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)

Icelandjack avatar Mar 01 '17 16:03 Icelandjack

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

Icelandjack avatar May 12 '17 13:05 Icelandjack

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) 

Icelandjack avatar May 20 '17 23:05 Icelandjack

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))

Icelandjack avatar May 20 '17 23:05 Icelandjack

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.

RyanGlScott avatar Jul 27 '17 20:07 RyanGlScott

How about the dependencies, Biapply is from semigroupoids which depends on bifunctors

Icelandjack avatar Jul 30 '17 17:07 Icelandjack

True. I see two options:

  1. For consistency with Lift from transformers, we just make it instance Biapplicative f => Biapplicative (Bilift f). This is a stronger constraint than is necessary, but then again, so are several other instances in the Applicative diaspora.
  2. We put Bilift in semigroupoids instead so that we can give the instance a Biapply context instead.

I don't have a strong opinion on which direction to take.

RyanGlScott avatar Jul 30 '17 17:07 RyanGlScott

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

Icelandjack avatar Jul 30 '17 17:07 Icelandjack