An Either functor by Mark Seemann
Either forms a normal functor. A placeholder article for object-oriented programmers.
This article is an instalment in an article series about functors. As another article explains, Either is a bifunctor. This makes it trivially a functor. As such, this article is mostly a place-holder to fit the spot in the functor table of contents, thereby indicating that Either is a functor.
Since Either is a bifunctor, it's actually not one, but two, functors. Many languages, C# included, are best equipped to deal with unambiguous functors. This is also true in Haskell, where Either l r
is only a Functor
over the right side. Likewise, in C#, you can make IEither<L, R>
a functor by implementing Select
:
public static IEither<L, R1> Select<L, R, R1>( this IEither<L, R> source, Func<R, R1> selector) { return source.SelectRight(selector); }
This method simply delegates all implementation to the SelectRight
method; it's just SelectRight
by another name. It obeys the functor laws, since these are just specializations of the bifunctor laws, and we know that Either is a proper bifunctor.
It would have been technically possible to instead implement a Select
method by calling SelectLeft
, but it seems generally more useful to enable syntactic sugar for mapping over 'happy path' scenarios. This enables you to write projections over operations that can fail.
Here's some C# Interactive examples that use the FindWinner
helper method from the Church-encoded Either article. Imagine that you're collecting votes; you're trying to pick the highest-voted integer, but in reality, you're only interested in seeing if the number is positive or not. Since FindWinner
returns IEither<VoteError, T>
, and this type is a functor, you can project the right result, while any left result short-circuits the query. First, here's a successful query:
> from i in FindWinner(1, 2, -3, -1, 2, -1, -1) select i > 0 Right<VoteError, bool>(false)
This query succeeds, resulting in a Right
object. The contained value is false
because the winner of the vote is -1
, which isn't a positive number.
On the other hand, the following query fails because of a tie.
> from i in FindWinner(1, 2, -3, -1, 2, -1) select i > 0 Left<VoteError, bool>(Tie)
Because the result is tied on -1
, the return value is a Left
object containing the VoteError
value Tie
.
Another source of error is an empty input collection:
> from i in FindWinner<int>() select i > 0 Left<VoteError, bool>(Empty)
This time, the Left
object contains the Empty
error value, since no winner can be found from an empty collection.
While the Select
method doesn't implement any behaviour that SelectRight
doesn't already afford, it enables you to use C# query syntax, as demonstrated by the above examples.
Next: A Tree functor.