Choice and Alternative traits refactor #1397
louthy
announced in
Announcements
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Two new traits have been added:
Choice<F>
Choice<F>
allows for propagation of 'failure' and 'choice' (in some appropriate sense, depending on the type).Choice
is aSemigroupK
, but has aChoose
method, rather than relying on theSemigroupK.Combine
method, (which now has a default implementation of invokingChoose
). That creates a new semantic meaning forChoose
, which is about choice propagation rather than the broader meaning ofCombine
. It also allows forChoose
andCombine
to have separate implementations depending on the type.The way to think about
Choose
and the inheritedSemigroupK.Combine
methods is:Choose
is the failure/choice propagation operator:|
Combine
is the concatenation/combination/addition operator:+
Any type that supports the
Choice
trait should also implement the|
operator, to enable easy choice/failure propagation. If there is a different implementation ofCombine
(rather than accepting the default), then the type should also implement the+
operator.ChoiceLaw
can help you test your implementation:It also tests the
Applicative
andFunctor
laws.Types that implement the
Choice
trait:Arr<A>
HashSet<A>
Iterable<A>
Lst<A>
Seq<A>
Either<L, R>
EitherT<L, M, R>
Eff<A>
Eff<RT, A>
IO<A>
Fin<A>
FinT<M, A>
Option<A>
OptionT<M, A>
Try<A>
TryT<M, A>
Validation<F, A>
Validation<F, M, A>
Identity<A>
IdentityT<M, A>
Reader<E, A>
ReaderT<E, M, A>
RWST<R, W, S, M, A>
State<S, A>
StateT<S, M, A>
Writer<A>
WriterT<M, A>
NOTE: Some of those types don't have a natural failure value. For the monad-transformers (like
ReaderT
,WriterT
, ...) they add aChoice
constraint on theM
monad that is lifted into the transformer. That allows for theChoice
andCombine
behaviour to flow down the transformer until it finds a monad that has a way of handling the request.For example:
ReaderT
can't handle the+
(Combine
) request, so it gets passed down the transformer stack, where theSeq
handles it. Resulting in:Similarly for
|
:The
Option
knows how to handle|
(Choose
) and propagates the failure until it gets aSome
value, resulting in:This is quite elegant I think, but it requires all monads in a stack to implement
Choice
. So, a good sensible default (for regular monads without a failure state), is to simply return the first argument (because it always succeeds). That allows all monads to be used in a transformer stack. This isn't ideal, but it's pragmatic and opens up a powerful set of features.Alternative<F>
Alternative
is aChoice
with an additionalMonoidK
. That augmentsChoice
withEmpty
and allows for a default empty state.AlternativeLaw
can help you test your implementation:It also tests the
Applicative
andFunctor
laws.Types that implement the
Alternative
trait:Arr<A>
HashSet<A>
Iterable<A>
Lst<A>
Seq<A>
Eff<A>
Eff<RT, A>
IO<A>
Fin<A>
FinT<M, A>
Option<A>
OptionT<M, A>
Try<A>
TryT<M, A>
Validation<F, A>
Validation<F, M, A>
Thanks to @hermanda19 for advocating for the return of the
Alternative
trait, I think I'd gotten a little too close to the code-base and couldn't see the wood for the trees when I removed it a few weeks back. The suggestion to make the trait have a semantically different method name (Choose
) re-awoke my brain I think! :DAny thoughts or comments, please let me know below.
This discussion was created from the release Choice and Alternative traits refactor.
Beta Was this translation helpful? Give feedback.
All reactions