purescript/purescript-filterable

Contravariant functor subclass

rightfold opened this issue · 4 comments

It is useful to have:

class (Contravariant f) <= Contrafilterable f where
  cfilter :: forall a. (a -> Boolean) -> f a -> f a
  cfilterCmap :: forall a b. (b -> Maybe a) -> f a -> f b
  cpartition :: ...
  cpartitionCmap :: ...

An example use case is my logging library, which currently provides this method as a function: https://pursuit.purescript.org/packages/purescript-logging/0.0.3/docs/Control.Logger#v:cfilter

Thanks, I've been thinking about this. Although I haven't tested it; I think we can define these in terms of Decidable. Something like:

cfilterCmap :: forall a b f. Decidable f => (a -> Maybe b) -> f b -> f a
cfilterCmap f = choose (maybe (Left unit) Right <<< f) conquer

I'm also thinking about a Profunctor version.

Very interesting! It seems Logger is an instance of Decidable.

I've been thinking about this for a while. If we look at the types:

choose :: (a -> b + c) -> f b * f c -> f a
altMap :: (b + c -> a) -> f b * f c -> f a
altMap f l r = map f (alt (map Left l) (map Right r))  

The difference here is that the function argument has been reversed.

Now if we compare partitionMap with choose:

choose       :: (a -> b + c) -> f b * f c -> f a
partitionMap :: (a -> b + c) -> f a -> f b * f c

The function argument is the same. If instead we reverse the function argument,
as in the difference between altMap and choose, we get a kind of forking
behaviour:

forkMap      :: (b + c -> a) -> f a -> f b * f c
partitionMap :: (a -> b + c) -> f a -> f b * f c

However, forkMap appears to be implementable just with cmap:

forkMap f = cmap (f <<< Left) &&& cmap (f <<< Right)

So now I'm a little confused 😄.

What if in addition to reversing the function argument, you swap out * for + in the result?