v0.5.0
is out!@zcabjro/either is a small js library that allows users to represent values that can be one of two types. A common use-case of
Either
is as a functional alternative to throwing exceptions. For example, you could capture a possible error state within the typeEither<Error, T>
. I use it in my validation library @zcabjro/vivalidate.
type Either<A, B> =
| Left<A, B>
| Right<B, A>;
While still in initial development, this version is a marked improvement for two reasons (one big, one small):
Either
as a union, we can more easily refine the type when inspecting the properties isLeft
and isRight
.Either<Error, string>
to Either<Error, number>
. This was down to my attempts to implement a LeftProjection
type similar to scala's. I haven't reached for it much in my projects so have decided to remove it in v0.5.0.There is a small downside accompanying these changes, which is that the type-system no longer captures that mapping from Left
will return a Left
and similarly that mapping from Right
will return a Right
.
right('Alice')
.map(s => s.length);
// returns Either<never, number>
Because of the new union implementation, when calling map
, both cases of the union must have compatible signatures. I expect to continue occasionally grappling with the topic and hopefully I'll discover a solution (though given it is far more beneficial to have good refinement from Either
to Left
/Right
, I'm happy with the trade-off).
Before, isLeft
and isRight
were methods that would refine the current instance of Either
to either Left
or Right
. This works great, but isn't as helpful in practice as discriminating a union. Here is an example:
declare const x:
Either<Error, string>;
// Before v0.4.0
if (x.isLeft()) {
const l: Left<Error> = x;
} else {
// ERROR!
const r: Right<string> = x;
}
// After v0.4.0
if (x.isLeft) {
const l: Left<Error> = x;
} else {
// OK!
const r: Right<string> = x;
}
This offers a lot more flexibility and saves us having to make redundant checks!
Taking inspiration from scala's Either
, I originally tried to emulate it's case class interface by exposing functions Left
and Right
that would create instances of their respective types. In retrospect, this introduces confusion between the types and helper functions, so the interface has been re-worked. Either
, Left
and Right
now refer to types while the functions left
and right
are available for creating instances.
LeftProjection
has been removed as its unsound implementation was causing problems and I haven't found a huge need for projecting to Left
so far in my projects. The goal was to support this functionality:
declare const either:
Either<string[], number>;
either
.left()
.map(xs => xs.join(','));
// returns Either<string, number>
I may end up looking to re-introduce it though with the benefit of hindsight.