How to compose multiple IO effects with Eff<RT> and a custom Runtime? #1544
Replies: 3 comments 3 replies
-
|
Hi Ilya, firstly thank you for sponsoring language-ext, it's very much appreciated and goes a long way to motivating me with the project. I'm still away at the moment, trying to squeeze the last drops out of the ski season! I'll be back home next Thursday (26th), so I'll be able to reply to this in-depth then. Hopefully some of the other experts here can help in the meantime 👍 |
Beta Was this translation helpful? Give feedback.
-
|
Hello, clarify me if I misunderstood something.
My guess is that you want to wrap these two methods like this: record DeviceId;
interface DeviceIOTrait
{
Task<Seq<DeviceId>> LoadDevicesAsync();
Task<DateTimeOffset> GetActivityAsync(DeviceId deviceId);
}
record DeviceIOImpl : DeviceIOTrait
{
public static readonly DeviceIOTrait Default = new DeviceIOImpl();
public Task<DateTimeOffset> GetActivityAsync(DeviceId deviceId) =>
throw new NotImplementedException();
public Task<Seq<DeviceId>> LoadDevicesAsync() =>
throw new NotImplementedException();
}
record MyRuntime : Has<Eff<MyRuntime>, DeviceIOTrait>
{
static K<Eff<MyRuntime>, DeviceIOTrait> Has<Eff<MyRuntime>, DeviceIOTrait>.Ask =>
pure<Eff<MyRuntime>, DeviceIOTrait>(DeviceIOImpl.Default);
}
I think you've done it already in your example, static class DeviceModule<RT> where RT : Has<Eff<RT>, DeviceIOTrait>
{
static Eff<RT, DeviceIOTrait> trait =>
+Has<Eff<RT>, RT, DeviceIOTrait>.ask; // + is equivalent to .As()
public static Eff<RT, Seq<DateOnly>> getAllActivities() =>
from dev in trait
from devices in liftIO(() => dev.LoadDevicesAsync())
from dates in devices.Traverse(getActivity) * BusinessLogic.toDatesOnly // * is equivalent to .Map()
select dates;
static Eff<RT, DateTimeOffset> getActivity(DeviceId deviceId) =>
from dev in trait
from activity in liftIO(() => dev.GetActivityAsync(deviceId))
select activity;
}
static class BusinessLogic
{
public static Seq<DateOnly> toDatesOnly(Seq<DateTimeOffset> activities) =>
activities.Map(a => DateOnly.FromDateTime(a.DateTime));
}
You specify the constaints on static class DeviceModule<RT>
where RT :
Has<Eff<RT>, DeviceIOTrait>,
Has<Eff<RT>, DirectoryIO> // for example
I haven't yet dealt with so called god-runtimes, but currently I don't see any way of splitting them and composing in one computation, as well as I don't see any problems with god-runtime: make it |
Beta Was this translation helpful? Give feedback.
-
|
To chime in on In your example with only two traits, it may not be a big deal, but if you find, for example, that you're implementing a lot of file-system based utilities that only care about That way, you do still have to define the "god-runtime" up front, but once you drill down to the actual application logic you find that nothing even knows about the concrete runtime type, and only cares about the minimal set of traits that it needs. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hello!
Dearest @louthy (*), I can't express how grateful I am for your work. This is the most incredible project in terms of both fun and growth for me as a software engineer.
* And other contributors, of course. For your questions, suggestions, donations. I love you!
I have a question about Eff/IO. Please, advise :)
Setup
LanguageExt v5.
I have two effectful operations:
And some pure business logic that works on the collected data — for example, reducing each
DateTimeOffsettoDateOnly:Goal
I want to follow the "impureum sandwich" pattern:
Something along these lines (pseudo-code):
Questions
1. Trait design — How should the traits (interfaces) on
RTbe defined so the runtime is injectable, similar to theFileIO/HasFile<RT>pattern in the docs?2. Composing the pipeline — How to properly
Traverse/Mapover the device sequence insideEffwhile keeping the IO layer flat (all reads upfront, then pure transforms)?3. Different runtimes — What if
LoadDevicesandGetActivityrequire different runtime capabilities (e.g. differentHas<RT, …>constraints)? How do you compose effects that depend on different parts of the runtime without creating a god-runtime that implements everything?Beta Was this translation helpful? Give feedback.
All reactions