purescript / purescript-filterable

Classes for filterable and witherable data structures
MIT License
25 stars 7 forks source link

Contravariant functor subclass #4

Open rightfold opened 8 years ago

rightfold commented 8 years ago

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

LiamGoodacre commented 8 years ago

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.

rightfold commented 8 years ago

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

LiamGoodacre commented 8 years ago

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 :smile:.

rightfold commented 7 years ago

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