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
Replace the entire type class hierarchy #93
Comments
Thanks John for the proposal. But you have a background that I seem to lack. Can you please paste some Scala code of the type classes you're thinking of? Just to make it clear what you're thinking of. |
The Scala versions are coming shortly in Scalaz 8, but in the meantime there are other versions available to review. |
There are some merits to the current type class design and off the top of my head:
This is also clear in the implementation of Monix's streaming abstractions — So my point is that when you do higher-kinded polymorphism, like we are doing by using these type classes, the important bit is being able to only use the restrictions required for the operation in question and no more. Therefore the separation between Of course, the question is — is this hierarchy not enough? In particular, maybe we need:
|
Thanks for opening. For some context, see this twitter thread: https://twitter.com/jdegoes/status/938051548843618304 Note specifically the criticism of
We've discussed (1) quite a bit, most recently in #71. We haven't discussed (2) though. Ideally, cats-effect would provide type classes which let both (1) and (2) be handled correctly in fs2 for all effect types. As such, I'm pretty sympathetic to re-examining our hierarchy and adding things like |
As for That said, I've spoke my piece. I've suggested the best possible design that I know of that supports non-leaky, composable applications that abstract over effect systems. I'm happy to contribute the Scalaz 8 design but don't really have energy to debate it. Do with these suggestions as you will! |
@mpilquist @jdegoes The two cases you point out there both stem from users assuming behavior which doesn't hold in general: either catching exceptions in pure code, or that the specific interrupt mechanism in an underlying effect type can somehow magically cross into generic code. The first point is pretty directly addressed: we can't really codify exception-catching into the laws, because a) it conflicts with Case 2 is more interesting, and it basically comes down to cats-effect's philosophy that the effect type is an atomic unit of work. fs2 inherits that philosophy (technically, it was a philosophy that Paul, Runar, and I came up with when sketching out fs2). Now, this does not mean that it is unlawful for an effect type to implement the cats-effect abstractions and provide a native interruption mechanism (see: Monix and Scalaz 8), but what it does mean is that any framework which solely works in terms of these abstractions will not understand the nature of those interrupts. If users take advantage of those interrupt mechanisms, the guarantees provided by higher level frameworks working through the abstractions may be corrupted. This is by design. Here's what it comes down to for me… I strongly believe that there are two semantic layers of abstraction in functional, composable effects: concurrency, resource safety, preemption, streaming, etc; and effect capture itself. While it is certainly possible for a framework to handle all of these in one lump (see: Scalaz 8), it requires extra complexity and imposes semantic costs on use-cases which don't care about concurrency or resource safety in the face of preemption. Thus, we have the split between cats-effect and fs2 (and other similar frameworks).
Only if you want concurrency to be represented directly in the effect library. I desperately want to not do this, since supporting parallelism means stepping away from the "atomic effect" concept, due to the fact that parallelism can only sanely be supported by an effect type which also has first-class preemption and resource-safe primitives.
Anyway, in summary, I'm not opposed to revising the hierarchy. And finally, to be clear, I'm not closed to the possibility that the division of abstractions (between atomic effects and more semantically rich parallel structures) is simply the wrong place to chop things up. (which is to say, I'm open to the possibility that Scalaz 8's philosophy of |
👍 I don't need to argue for the Scalaz 8 / PureScript / Haskell model, since I think this debate will be settled by the community based on merit (one way or the other). However, I would point out that it's not just One has to loosen some of the laws for
This is not strictly true. If you decided to change
(One could also add these guarantees to |
Can you be more specific about this? Aside from the cases @mpilquist raised (which are both user expectation issues, not fs2 issues), what unlawful or unspecified behavior is fs2 relying on?
This is true, and it is compelling, though to be clear that concept of a finalizer has no real distinction from |
As recently as yesterday, the documentation for FS2 made guarantees that could not be satisfied by lawful instances for the Cats-effect type class hierarchy (you cannot make stronger guarantees than your base monad!). For the documentation to be correct, catching during map/flatMap and atomicity are both required. One can argue the documentation is broken, but I’d rather argue the capabilities required to build libraries like FS2 are simply missing from the hierarchy and can be added at no cost to users who don’t care about them. |
We now have a type class hierarchy that addresses many of the concerns raised. Would like to do some cleanup in the issue tracker, plus if there's new discussion to be had, I'd rather have us restart from scratch, participants being able to link to previous comments, otherwise engaging in old conversations is too noisy imo. Thanks @jdegoes and all for your suggestions, it helped. |
Happy to help! |
The existing Cats Effect type class hierarchy was a good proof-of-concept and demonstrated the existence of demand for library authors to abstract over effect monads.
Unfortunately, it is not possible to build resource safe, composable applications on the existing type class hierarchy. At the root of this problem is the lack of an abstraction for resource safety.
In the same way that all languages with exceptions have a
try
/catch
/finally
construct, all effect monads that support failure must have a similar analogue that can be used by higher-level libraries to ensure resource safety.In other words, something like
MonadBracket
needs to be a super class of all effect monads.The lack of such a construct has created libraries like FS2 which (a) depend on unlawful behavior, and (b) depend on unspecified behavior. This is false abstraction, not actual abstraction as per the original design goals of this project, and the very antithesis of principled functional programming.
I propose to replace the entire existing type class hierarchy by three type classes:
MonadBracket
, which extendsMonadError
;MonadFork
, which extendsMonadBracket
; andMonadIO
, which subsumes the best parts ofSync
/Async
and extendsMonadBracket
andMonadFork
.These are necessary and sufficient abstractions for resource-safe, concurrent, composable functional applications, and other functionality should be left to the underlying implementations.
The text was updated successfully, but these errors were encountered: