-
Notifications
You must be signed in to change notification settings - Fork 57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Explore Merging Aync and IO into a unified construct #709
Comments
Thank you for the details @Luka-J9! There's also this relevant comment by @steinybot in #contributors:
I was sympathetic to merging
Yeah, parametrizing effects is simpler in Kyo. I'd reserve this kind of pattern for libraries and advanced users, though. A major trap I see with people using tagless final is over abstraction. If using
That should be the case for most of the people adopting Kyo initially since many will have experience with other effect systems. That's a product of the low composability of other effect systems, though. They have to bundle async in the same monad that suspends side effects because that's the only option. I don't like the renaming from |
Hey @fwbrasil! Chewing on this, some additional thoughts:
As it relates to this I think the big difference between the Async/IO and Abort is that Async is a comprised of IO where as Abort is independent. Said another way Looking at prior art it seems like the need for this differentiation isn't unique to Kyo. While I don't totally understand the differentiation is strictly necessary (I am but a humble user) I think the name change would be more clear and easier for user I think having the name still contain IO might make it more clear e.g. SyncIO instead of IO and Async -> IO |
I could be totally wrong but I think the reason |
I think your intuition is in the right direction. opaque type Async <: (IO & Async.Join) = Async.Join & IO It's possible to have
Thanks for giving it some more thought :) I like |
Exactly! 🎯 We could, for example, suspend all async operations in a custom effect and then introduce
That's because |
I'm on the fence with this. Implementations and interfaces have the opposite requirements. Liskov substitution principle (LSP) says that method parameter types should be contravariant and return types covariant. The most useful, i.e. most substitutable w.r.t LSP, implementations are those that have the most general parameter types and most specific return types. In other words, accept Async and return IO. However, when the implementation is taking a parameter that is itself an abstract type then that is in contravariant position and it should be as general as possible which means it has the most specific contravariant types and most general covariant types. In other words, accept an implementation of an interface that accepts IO and returns Async. That’s a bit of a confusing way to say it but this mostly demonstrates what I mean:
So I don't know if I quite agree that making
That seemed appealing at first but that might just be annoying. Would it make sense to go the opposite way and encourage that whenever the
That's cool. Seemed impossible at first but now that I think about it, it is just a hole that gets filled when run which then flows through all the places that use it and the update functions. |
Just to be clear @steinybot the suggestion is that the type The reason for this may be minor, but I'd argue important. If you learn about effect systems outside of Kyo, or learn about effect systems in the abstract, the primitive you expect to use as the "default" is some form of Admittedly it could be just me, but I definitely spun my wheels for a while before I realized that Async was the "biggest" type and that was the type I should be using for my case Given that most examples and discussions around effects use Admittedly all of this can perhaps be resolved with some documentation or educational materials. I just think it ends up being an unnecessary stumbling block for folks |
Spiel incoming lol. Sorry, I tried cutting it down. Damn these strong but loosely held opinions of mine... My previous comment was about changing the "default" which I don't think we should do. Our API and docs should use the most specific type. The fact that most examples just have Ease of learning is definitely very important. I find the easiest things to learn are those that have simple rules that can be combined in predictible ways. This is especially true for people who might be learning effects for the first time. I'd argue that merging I agree that we need to try and help people who are coming from other effect systems, especially Cats Effect and ZIO. We might be able to use some existing concepts to scaffold learnings in Kyo but I'm not convinced that this is a good idea. The differences can be subtle but they are significant. Changing I think the idea of "bigger" effects needs to be unlearned. There aren't bigger effects. I know you put that in quotes so maybe you don't mean it literally. Yes you probably need to learn In summary, I think we should:
|
Let me try to phrase it in a different way. I think we have three main concerns:
It seems we're aligned regarding the need to cater to all these audiences. We don't want to merge the effects since that'd be an important limitation for advanced users but we can mitigate the implications for the other audiences. A naming change like @Luka-J9 suggested paired with documentation suggesting the equivalent of I must be honest that naming isn't my forte so please feel free to suggest naming schemes! Suggestions I could identify in our discussions:
Reducing the tracking overhead is a major challenge for usability. We need to find a good balance that works well for the different potential users of the library. It seems the majority of the users wouldn't benefit from the additional tracking if we don't include |
How about
|
Speaking strictly from the user perspective of Kyo, I much prefer
Perhaps users can also make an alias if they so choose... |
Huh I did not catch onto that either although I haven't fully groked the kernel stuff yet. So does that mean that it is
This is a great point that I agree with completely. I like |
To be more precise, I think it's confusing because of the mental model of how other effect systems work. A computation of type This approach enables A lot of work has gone into enabling that. For example, other effect systems typically rely on mutable collections used across multiple transformations, which seems fine if the base monad includes I agree this distinction doesn't matter for most users but, although we try to keep a down-to-earth approach to usability, ensuring Kyo is able to represent a wide range of computations with fundamental properties like this one can yield interesting results in the long term. For most users, I'm hoping our recommendation of
Yes, the kernel also introduces internal suspensions to ensure stack safety. The internal effect used for that is called
I agree with the rename to |
Any concerns regarding proceeding with the renaming from |
For what my opinion is worth, I think the most user friendly and recognizable naming would be
|
Every time I pick this up to work on, I have doubts if it's a good change 😅 Thank you everyone for the feedback but I think the current naming seems the best to express what those effects do. I've created a PR better documenting this aspect and introducing an Please share if you feel strongly about this but I think we could close this once the PR lands. |
Summary from larger conversation on discord:
I may be confusing that there is a different pending effect for Async as with IO. In other effect systems and by (at least my) intuition usually IO is considered the "biggest" effect type (as in capable of doing the most things). With Async and IO split you can get significantly different effect types depending on which actions are called.
Motivating example:
The addition of a sleep changes the type from IO -> Sync, which changes the signature from
Int < IO
->Int < Async
. This is a pretty minor change in code bases where you control the signature, but may be difficult to satisfy in instances where the modification is not within the users control.Copying some background reasoning from @fwbrasil in the discord thread
Alternative solutions:
If it's determined that this split between Async and IO must continue and the concepts must be distinct - and alternative path is to rename the current values. Exact naming may change but it may be better to rename
Async
->IO
and rename the currentIO
->Sync
(or something else). I believe this keeps in line with "IO" being the biggest type while also preserving the reasoning for splitting the original types. Additionally - this might be me - I've always associated Async with being under the umbrella of IOThe text was updated successfully, but these errors were encountered: