Skip to content

Generalised Partition for all Fallible monads

Pre-release
Pre-release

Choose a tag to compare

@louthy louthy released this 07 Nov 23:19
· 402 commits to main since this release

In this release there are now generalised Partition, Succs, and Fails methods (and their equivalent Prelude functions: partition, succs, and fails) that work with any Foldable of Fallible monads.

This is the main Partition extension:

  public static K<M, (Seq<Error> Fails, Seq<A> Succs)> Partition<F, M, A>(
      this K<F, K<M, A>> fma)
      where M : Monad<M>, Fallible<M>
      where F : Foldable<F> =>
      fma.Fold(M.Pure((Fails: Seq.empty<Error>(), Succs: Seq.empty<A>())),
               ma => ms => ms.Bind(
                         s => ma.Bind(a => M.Pure((s.Fails, s.Succs.Add(a))))
                                .Catch(e => M.Pure((s.Fails.Add(e), s.Succs)))));

So, if your F is a Iterable and your M is an IO (so Iterable<IO<A>>), then you can run Partition on that to get a IO<(Seq<Error> Fails, Seq<A> Succs)>. Obviously this will work with any Foldable types you have made too (as well as all the built-in ones: Arr, Lst, Iterable, Seq, Set, HashSet, etc.) -- with any effect type as long as its Fallible and a Monad. Including Fallible types with a bespoke E error value (but you will have to specify the generics as it can't infer from the arguments alone).

For those who know the extensions Partition, Some, Rights, etc. from v4, this generalises the idea completely.

I have also added a Partition extension and partition prelude function that works with any Foldable structure. Unlike the Partition function that works with Fallible types (which partitions on success or failure), this one takes a predicate which is used to partition based on the true / false return. It's like Filter but instead of throwing away the false values, it keeps them and returns a True sequence and False sequence:

public static (Seq<A> True, Seq<A> False) Partition<T, A>(this K<T, A> ta, Func<A, bool> f)
    where T : Foldable<T> =>
    T.Partition(f, ta);

All foldables get a default implementation, but it's possible to override in the trait-implementation (for performance reasons mostly).

Finally, I've added unit-tests for the Foldable default-implementations. Anybody who's been following along will know that the Foldable trait only has two methods that need implementing and over 50 default implementations that we get for free. So, the unit test makes sure they work! (which should guarantee they work for all foldables as long as the two required method-implementations are correct).

I'm considering how I can refactor those unit-tests into a FoldableLaw type that will work like MonadLaw, FunctorLaw, etc. But that's not there yet.