ekmett/contravariant

newtype F m f a = F (f a -> m)

Icelandjack opened this issue · 4 comments

Something like this generalizes

newtype F m f a = F (f a -> m)

instance Functor f => Contravariant (F m f) where
  contramap :: (a' -> a) -> (F m f a -> F m f a')
  contramap f (F g) = F (\fa -> g (fmap f fa))

instance (Monoid m, Functor f) => Divisible (F m f) where
  conquer :: F m f a
  conquer = F $ const mempty

  divide :: (a -> (b, c)) -> (F m f b -> F m f c -> F m f a)
  divide split (F left) (F right) = F $ \(fmap split -> fbc) ->
    left (fst <$> fbc) <> right (snd <$> fbc)

where

newtype Predicate a = Predicate_ (F All Identity a)

newtype Comparison a = Comparison_ (F Ordering V2 a)

newtype Equivalence a = Equivalence_ (F All V2 a)

One of these days I'm expecting to see you write

newtype NewType a = NewType (Deriving Kitchen Sink Plus NewType a) deriving (.*)

F m f a = Flip (Costar f) m a, have you tried implementing Divisible (Flip p m) with some constraints on p?

Edit: I think you need forall a. Monoid (p a m), annoyingly.

Also this first needs an instance Profunctor p => Contravariant (Flip p a), which could only live in the profunctor package because profunctor depends on contravariant, but that would be an orphan instance...

@ocharles Be careful!

@sjoerdvisscher My gut tells me it should be its own thing, forall xx. Monoid (p xx m) could be a way forward once we have quantified constraints or maybe there is a totally different constraint that makes sense, I would have to think about it.


I wasn't successful in generalizing it to Decidable, lose seems to require a Comonad constraint but I got stuck on choose

instance (Monoid m, Comonad f) => Decidable (F m f) where
  lose :: (a -> Void) -> F m f a
  lose f = F (absurd . f . extract)

  choose :: (a -> Either b c) -> (F m f b -> F m f c -> F m f a)
  choose split = ???

The part I'm unsure how to achieve (generically) is producing LT and GT,

choose :: forall a b c. (a -> Either b c) -> (Comparison b -> Comparison c -> Comparison a)
choose split (Comparison left) (Comparison right) = Comparison (\a a' -> split a * split a')
  where

  (*) :: Either b c -> Either b c -> Ordering
  Left  b * Left  b' = left  b b'
  Right c * Right c' = right c c'
  Left{}  * Right{}  = LT
  Right{} * Left{}   = GT

one way (that requires an Ord constraint for b, c) is compare Left{} Right{} = LT but meh

In my experience, if you have an Applicative instance that requires a Monoid, then there can be an instance for Alternative if you switch to a semiring, where the multiplicative monoid gives the Applicative instance and the additive monoid the Alternative one.

Maybe the same works here?