Implementing ToIO on "custom" Transformer Stacks #1435
Replies: 3 comments 7 replies
-
|
Doesn't it always match to default, because you are switching on |
Beta Was this translation helpful? Give feedback.
-
|
I see that you have a generic parameter of So, make sure you've overrided the correct method in the trait-implementation of |
Beta Was this translation helpful? Give feedback.
-
|
So, part of the issue here is that However, So, to make this work, first implement these methods for public record FlowError
{
public static FlowError FromError(Error err) => throw new System.NotImplementedException();
public Error ToError() => throw new System.NotImplementedException();
}You would need these anyway, because you really want to capture exceptions from the inner Then you can do the public record Flow<A>(ReaderT<FlowState, EitherT<FlowError, IO>, A> executeFlow)
: K<Flow, A>;
public static class FlowExtensions
{
public static Flow<A> As<A>(this K<Flow, A> ma) =>
(Flow<A>)ma;
public static IO<Either<FlowError, A>> Run<A>(this K<Flow, A> ma, FlowState state) =>
ma.As()
.executeFlow
.Run(state)
.Run()
.Catch(_ => true, e => Either<FlowError, A>.Left(FlowError.FromError(e)))
.As();
}
public class Flow :
Monad<Flow>,
Readable<Flow, FlowState>,
Fallible<FlowError, Flow>
{
static K<Flow, B> Monad<Flow>.Bind<A, B>(K<Flow, A> ma, Func<A, K<Flow, B>> f) =>
new Flow<B>(ma.As().executeFlow.Bind(x => f(x).As().executeFlow));
static K<Flow, B> Functor<Flow>.Map<A, B>(Func<A, B> f, K<Flow, A> ma) =>
new Flow<B>(ma.As().executeFlow.Map(f));
static K<Flow, A> Applicative<Flow>.Pure<A>(A value) =>
new Flow<A>(ReaderT.pure<FlowState, EitherT<FlowError, IO>, A>(value));
static K<Flow, B> Applicative<Flow>.Apply<A, B>(K<Flow, Func<A, B>> mf, K<Flow, A> ma) =>
new Flow<B>(mf.As().executeFlow.Apply(ma.As().executeFlow).As());
static K<Flow, A> Readable<Flow, FlowState>.Asks<A>(Func<FlowState, A> f) =>
new Flow<A>(ReaderT.asks<EitherT<FlowError, IO>, A, FlowState>(f));
static K<Flow, A> Readable<Flow, FlowState>.Local<A>(Func<FlowState, FlowState> f, K<Flow, A> ma) =>
new Flow<A>(ReaderT.local(f, ma.As().executeFlow));
static K<Flow, A> Fallible<FlowError, Flow>.Fail<A>(FlowError error) =>
new Flow<A>(ReaderT.lift<FlowState, EitherT<FlowError, IO>, A>(EitherT<FlowError, IO, A>.Left(error)));
static K<Flow, A> Fallible<FlowError, Flow>.Catch<A>(
K<Flow, A> fa,
Func<FlowError, bool> Predicate,
Func<FlowError, K<Flow, A>> Fail) =>
new Flow<A>(
ReaderT<FlowState, EitherT<FlowError, IO>, A>.AsksM(
state =>
{
// Catch and process IO errors
var io = fa.As()
.executeFlow
.Run(state)
.Run()
.Catch<IO, Either<FlowError, A>>(
Predicate: (Error e) => Predicate(FlowError.FromError(e)),
Fail: (Error e) => Fail(FlowError.FromError(e)).As().Run(state));
// Catch and process FlowErrors
io = io.Bind(ea => ea switch
{
Either.Left<FlowError, A> (var l) => IO.pure(Either<FlowError, A>.Left(l)),
Either.Right<FlowError, A> (var r) => IO.pure(Either<FlowError, A>.Right(r))
});
// Return
return EitherT<FlowError, IO, A>.Lift(io);
}));
static K<Flow, A> MonadIO<Flow>.LiftIO<A>(IO<A> ma) =>
new Flow<A>(
ReaderT.lift<FlowState, EitherT<FlowError, IO>, A>(
new EitherT<FlowError, IO, A>(
IO.lift(envIO =>
{
try
{
var result = ma.Run(envIO);
return Either<FlowError, A>.Right(result);
}
catch (Exception e)
{
// When lifting random IO computations into Flow, always true to convert
// the exceptions into FlowError
return Either<FlowError, A>.Left(FlowError.FromError(e));
}
}))));
static K<Flow, IO<A>> MonadIO<Flow>.ToIO<A>(K<Flow, A> ma) =>
new Flow<IO<A>>(
ReaderT.asksM<EitherT<FlowError, IO>, FlowState, IO<A>>(
state =>
EitherT<FlowError, IO, IO<A>>.Right(
ma.As()
.executeFlow
.Run(state)
.As()
.Run()
.Map(ea => ea switch
{ // We must throw here so we can catch later
Either.Left<FlowError, A> (var l) => l.ToError().Throw<A>(),
Either.Right<FlowError, A> (var r) => r
})
.As())));
}Notice:
I haven't run any of the code above, so I don't know if it definitely works, but it should do. By the way, most of these issues go away if you just use the This is with public static class FlowExtensions
{
public static Flow<A> As<A>(this K<Flow, A> ma) =>
(Flow<A>)ma;
public static IO<A> Run<A>(this K<Flow, A> ma, FlowState state) =>
ma.As()
.executeFlow
.Run(state)
.As();
}
public class Flow :
Monad<Flow>,
Readable<Flow, FlowState>,
Fallible<Flow>
{
static K<Flow, B> Monad<Flow>.Bind<A, B>(K<Flow, A> ma, Func<A, K<Flow, B>> f) =>
new Flow<B>(ma.As().executeFlow.Bind(x => f(x).As().executeFlow));
static K<Flow, B> Functor<Flow>.Map<A, B>(Func<A, B> f, K<Flow, A> ma) =>
new Flow<B>(ma.As().executeFlow.Map(f));
static K<Flow, A> Applicative<Flow>.Pure<A>(A value) =>
new Flow<A>(ReaderT.pure<FlowState, IO, A>(value));
static K<Flow, B> Applicative<Flow>.Apply<A, B>(K<Flow, Func<A, B>> mf, K<Flow, A> ma) =>
new Flow<B>(mf.As().executeFlow.Apply(ma.As().executeFlow).As());
static K<Flow, A> Readable<Flow, FlowState>.Asks<A>(Func<FlowState, A> f) =>
new Flow<A>(ReaderT.asks<IO, A, FlowState>(f));
static K<Flow, A> Readable<Flow, FlowState>.Local<A>(Func<FlowState, FlowState> f, K<Flow, A> ma) =>
new Flow<A>(ReaderT.local(f, ma.As().executeFlow));
static K<Flow, A> Fallible<Error, Flow>.Fail<A>(Error error) =>
new Flow<A>(ReaderT.lift<FlowState, IO, A>(IO.fail<A>(error)));
static K<Flow, A> Fallible<Error, Flow>.Catch<A>(
K<Flow, A> fa,
Func<Error, bool> Predicate,
Func<Error, K<Flow, A>> Fail) =>
from state in Readable.ask<Flow, FlowState>()
from result in fa.MapIO(io => io.Catch(Predicate, e => Fail(e).Run(state)))
select result;
static K<Flow, A> MonadIO<Flow>.LiftIO<A>(IO<A> ma) =>
new Flow<A>(ReaderT.liftIO<FlowState, IO, A>(ma));
static K<Flow, IO<A>> MonadIO<Flow>.ToIO<A>(K<Flow, A> ma) =>
new Flow<IO<A>>(ma.As().executeFlow.ToIO().As());
}Notice how much simpler |
Beta Was this translation helpful? Give feedback.

Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Hi, I've been messing around (dipping my head into) with Language.Ext for a while and my next project is retries (or unlocking the potential of the IO stuff).
Originally I had the following setup (see discussion):
record Flow<A>(StateT<FlowState, EitherT<Error, IO>, A> executeFlow) : K<FlowEither, A>However, I read in Discussion 1269 that the
StateTtransformer cannot work around the interface restriction ofMonadIOin a senseful ("magicless") way, so for now I exchangedStateTwithReaderT(as it would be sufficient for my main usecase).->
record Flow<A>(ReaderT<FlowState, EitherT<FlowError, IO>, A> executeFlow)Still I'm getting the error that
ToIOis not implemented, presumably because it's also not there onEitherT.Following Paul's example in the discussion, which can also be found here, I tried implementing it on
Flowmyself:It compiles! But it always returns a wrapped bottom error, as the intermediate result is wrapped in
Pureand the pattern matching does not seem to work.While I continue banging my head at this; Is this going into the right (hehe) direction?
Apologies if there is crucial code missing, I think all of the referenced functions are coming from the core library.
Cheers
Olli
Beta Was this translation helpful? Give feedback.
All reactions