New trait: Fallible #1342
louthy
announced in
Announcements
Replies: 1 comment 2 replies
-
Made me smile, as about six years ago, when I was just starting on my FP journey, I saw Scott Wlaschin's video on Railway Oriented Programming, and implemented something similar in C#. I didn't realise it at the time, but I was re-inventing the I named it Fallible, as I liked the name 😁 |
Beta Was this translation helpful? Give feedback.
2 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
In Haskell there's a trait called
MonadFail
for monadic types to raise errors. It's not particularly effective as most tend to avoid it. I wanted to create a trait (for types that can fail) that's effective and could help standardise error handling.That's the new
Fallible<E, F>
andFallible<F>
trait type...Fallible
Fallible<E, F>
- can have a parameterised failure valueE
for structureF
(usually a functor, applicative, or monad)Fallible<F>
is equivalent toFallible<Error, F>
- which simplifies usage for the commonly usedError
typeAnything that is fallible must implement:
Fail
is for the raising of errorsCatch
can be used to catch an error if it matches apredicate
; and if so, it runs thefail
function to produce a new structure (which may also be failing, but could be used to rescue the operation and provide a sensible succeeding default).Fallible
moduleIn the
Fallible
module there are functions to raise failures:fail
raises the parameterised error typeserror
raises theError
typeBecause the traits are all interfaces we can't use
operator |
for error handling (the operators can still be used for concrete types, likeEff<A>
,IO<A>
, etc.) -- and so there are now lots ofCatch
extension methods for catching errors inFallible
structures. You can view them here.Prelude
The
Prelude
now has:So, for example, you can now construct any type (as long as it implements the
Applicative
trait) usingpure
:And you can construct any type (as long as it implements the
Fallible<E, F>
trait) usingfail
or witherror
(whenFallible<F>
):Types that have been made
Fallible
IO<A>
Eff<RT, A>
Eff<A>
Either<L, R>
EitherT<L, M, R>
Fin<A>
Option<A>
OptionT<M, A>
Try<A>
TryT<A>
Validation<F, A>
ValidationT<F, M, A>
Which means you can use
.Catch(...)
on all of those types now. For example:IO
changesIO.Pure
has been renamedIO.pure
Some
andNone
in a discriminated union)IO.Pure
doesn't create anIO<A>
type with aPure
case, it constructs a lambda that returns anA
IO.Fail
has been renamedIO.fail
(see above)Eff
running extensionsThe various
.Run*
methods forEff<A>
andEff<RT, A>
have been made into extension methods that work withK<Eff, A>
andK<Eff<RT>, A>
. That means if you end up with the more abstract representation ofEff
you can run it without calling.As()
first.I'll be doing this for other types that are 'run'.
I have also tidied up some of the artefacts around the
MinRT
runtime used byEff<A>
. BecauseEff<A>
is now backed by a transformer stack withIO<A>
as its inner monad, theMinRT
doesn't need to carry any IO environment any more, so I've removed it fromMinRT
, makingMinRT
into a completely empty struct. This removes some constraints from theRun*
extensions.Prelude.ignoreF
andFunctor.IgnoreF
The prelude function
ignoreF
and equivalent extension method toFunctor<F>
,IgnoreF
are the equivalent of calling.Map(_ => unit)
to ignore the bound-value of a structure and instead returnunit
.Transducers removed
I have removed the Transducers completely from
v5
. They were originally going to be the building blocks of higher-kinds, but with the new trait-system I don't think they add enough value, and frankly I do not have the time to bring them through thisv5
release process (which is already a mammoth one)! As much as I like transducers, I think we can do better with the traits system now.Not needed traits removed
The following traits have been removed:
HasCancel
- This was used in theAff
monad and now isn't needed because theIO
monad has its own environment which carries the cancellation tokenHasFromError
- was used by the transducers, so not neededHasIO
- was used by theMinRT
runtime, which isn't needed anymoreHasSyncContextIO
- as aboveNew Sample
Those of you who are subscribed to my blog at paullouth.com will have seen the first newsletter this week. It took a while to get off the ground because I refused to use the terrible Mailgun integration in GhostCMS.
Instead I rolled my own, which I've been working on the past few days. So it was an opportunity to test out the effect system and trait system. I took it as far as it can go and the entire application is trait driven. Only when you invoke the application do you specify what monad and runtime to use.
This is the main operation for generating the newsletter and emailing it out to all of the members:
Note how the computation being run is entirely generic:
M
. Which is constrained to be aMonad
,Fallible
, andStateful
. The state isRT
, also generic, which is constrained to have various IO traits as well as aConfig
andHttpClient
state. This can be run with any type that supports those traits. Completely generic and abstract from the underlying implementation.Only when we we pass the generic argument to
Send<>
do we get a concrete implementation:Here, we run the
newsletter
operation with anEff<Runtime>
monad. But, it could be with any monad we build.Importantly, it works, so that's good :)
Source code is here . Any questions, ask in the comments below...
This discussion was created from the release New trait: Fallible.
Beta Was this translation helpful? Give feedback.
All reactions