-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
[Triage] Break out MTL to a separate library #1603
Comments
So, I know I suggested this (and if it happens, I vote for naming it “Transmogrifier”), but I do have some reservations. One, as @mpilquist raised, is “where do we draw the line?” I don’t want to end up in a situation where figuring out what comes from here vs Cats is non-obvious. Another is that I worry about making transformers available without these type classes. Specific transformer stacks already get hardcoded too often. If we make it harder to get the type classes that abstract over them, it’ll happen even more. On the plus side, being able to change encoding, etc. independently of breaking changes to Cats seems useful. I am pro-modularity in general. |
My current view on this is "MTL" type classes (this naming is unfortunate, they're really just type classes and shouldn't be privileged in any way but it gives a name to the problem we're referring to sooo...) is still in the "design process" for Scala. We have the usual subtype encoding which doesn't work, we have Scato, and we also have @edmundnoble 's capabilities-based design. In that sense perhaps it should be split out for the same reason https://github.com/effects4s is its own thing. On the flip side "where do we draw the line" is a very good point. On principle "MTL" ~= "any type class that shares a superclass with any other type class" But people seem to be doing fine with the current encoding up until they hit 🤷♂️ |
@adelbertc How about this: trait MTL[Type[_], Capability[_]] {
def injectCapability[A](capability: Capability[A]): Type[A]
def useCapability[A, B](type: Type[Capability[A]]): Type[A]
}
That should at least tell us how to determine what an MTL type class is. I am of the opinion that modular ( Also I am of the opinion that if we are to provide laws for these, we should use Scato because otherwise users are locked out of defining new type classes with new laws for an already extant set of operations. |
Initial thoughts since it's getting late here, but seems like an interesting approach. It's different enough however that if we do go down this route I'd like to see it outside of Cats, which would also open the door to using an alternative encoding. Now the hard part: what classes in Cats would be removed in light of this? |
IMO we have:
Not sure if I missed any. |
What of |
|
👎 to dropping |
@mpilquist Can you please provide your reasons? Edit: you mention 1.0. Are you saying these should wait for a later version to be removed or that you have reservations about removing them? |
Utility wise, |
Very valid concerns. Two points: a) |
IMO |
Only in the effects4s formulation. That is not fundamental. There is a lawful definition of |
@djspiewak I am fairly sure we are talking about effects4s here. More importantly, |
Are we? If we are, then
It really isn't (the distinguisher). My point is that Edit: What you're saying (regarding |
@djspiewak This is what I'm arguing against. You seem to be arguing about whether or not MonadError is a valid formulation of Catchable; I think this is entirely irrelevant to whether or not Catchable needs to exist in effects4s. And if Evaluable does, then Catchable needs to say something about it. b) fail[X](ex) <-> fail[IO[Y]](ex) // parametricity
fail[X](ex).map(f: X => IO[Y]) <-> fail[IO[Y]](ex) // map is equal, because there's no value
fail[X](ex).map(f: X => IO[Y]).join <-> fail[IO[Y]].join // add join to both sides
fail(ex).flatMap(f) <-> fail(ex) // join is equal, because there's no inner value to flatten effects from c) Here's my point: If you are not using an effect capture library, you do not care about side effects, and so you're doing MTL; then Edit: If this makes my position any clearer: I do not think that everyone should stop using MonadError and swap it out for Catchable. I apologize if I gave that impression. |
And yet, oddly,
No, you need a formulation of
Of course this is true. But the converse is not.
And this is the crux of the matter: I don't think transmogrifier is useful anyway. MTL is broken in cats. Fine. MTL has been broken in scalaz for years and people lived with it. MTL could be a lot better, and that would be awesome. Trying to move everything MTL-ish out of cats removes things from cats that are useful outside the landscape of MTL, and trying to have this argument now while we're (ostensibly) attempting to nail down cats 1.0 is just really strange timing. Edit: Maybe I'm just being too negative. But I think the problem with this whole thing is trying to draw some arbitrary line between typeclasses which are only useful for MTL and typeclasses which are more broadly applicable. Everyone is going to have a slightly different answer to that question. |
"MonadError is needed for effect libraries unless we want to have a proliferation of precisely isomorphic typeclasses with identical interfaces and laws. I see no reason that this sort of proliferation would be desirable in any way." Not that anything is wrong with To address your point on the subject of this thread (MTL being removed from cats in 1.0.0): For the record, I do not blame you for being negative; I haven't talked to a single person other than @sellout and @wedens who thinks this is worth doing. Nearly everyone else is of the opinion that "cats just needs to be 'as-good' as Scalaz". Personally, I think that's absolute shit. We are not Scalaz, we never have been and never will be. The reason this library even exists is (ostensibly) to make experiments beyond Scalaz, and try to find the best way FP features can be encoded in Scala. More importantly, by resigning ourselves to this "oh managers can only approve x libraries per month", "oh adding a dependency is hard" negativity and keeping more and more in cats we are giving up on having modular FP libraries, which in the long term is crucial to the prospects of FP in Scala. |
Oh… I didn't realize @alexandru had made I prefer to formulate things in the opposite direction, with effect suspension specializing
There's basically two different ways you can think of I'm fine with moving out
Agreed. I think it's important that the broad cats ecosystem supports the functionality that people have come to expect from the scalaz ecosystem as a whole, but the shape taken by that functionality and whether that functionality is in cats-core or in something like transmogrifier is not something that should be set in stone. Scalaz is exceptionally monolithic; a design decision which I believe was a mistake.
It's not so much about "we can only approve x libraries per month" and more about "the JVM has no solution to the version diamond problem". The more dependencies you have (transitively), the higher the odds that someone has been lazy and not updated to the latest incompatible (but not in a way which affects them) version of a particular artifact, resulting in a dependency clash which cannot be resolved downstream without risking runtime link errors. Jigsaw had a chance to address this problem, and they… didn't. Despite people begging them to. :-/ Now the flip side of this is that if you shove everything into a single monolith, your surface area for incompatibility becomes quite vast, meaning that minor and mostly-irrelevant changes require versioning as a breaking change, despite the majority of the surface area being fully compatible. This is precisely why the ecosystem of So in summary, versioning is terrible. Blow it all up. Become a hermit. Like Rob. There's no right answer here. But when in doubt, I agree that we should err on the side of modularity. Even though we don't have real modules, it at least gives us more flexibility in the long run. |
If |
I'm going to ask a stupid question. Please tell me if I'm being absolutely crazy here. Why does the MTL encoding have to use subtyping? I don't actually remember why exactly this is. The main typeclass hierarchy uses subtyping because implication produces ambiguity in the presence of diamonds, but MTL doesn't have that problem (since it does have diamonds). The quasar codebase has a number of "underscore variants" of the major MTL typeclasses. Using this encoding would provide a very natural separation between "MTL |
@djspiewak I think it was already discussed here. I do the same thing as quasar. Duplicate mtl typeclasses without subtyping. Works for me just fine. |
@wedens That discussion seems to hang up on "but I want to get a |
@djspiewak The MTL type class encoding I have been advocating is Scato, with the Monad constraints removed. E.g., This discussion seems to be edging toward design choices for transmogrifier, rather than cats; perhaps I should make an empty repo where these discussions would be more able to thrive? Edit: This is my inspiration, for the moment: https://ro-che.info/articles/extensible-effects |
@kailuowang The evils of democracy ;) I personally am in favor of taking all of the aforementioned type classes out of cats, with |
I discount my own opinion here, because I am going to use these type classes no matter what I have to do. They could each be in a separately-versioned dependency, and I’d add them all. So, my own inclination is to have |
@sellout Having |
@edmundnoble I think this plan has a good chance to be acceptable for most people (also a 👍 from me). Do you want to create a new issue specifying this plan so that we can have a simple thumbs up /down vote on it? |
@kailuowang Done. |
Closing this thanks to decision made at #1616 |
The idea originated from the gitter discussion here
So here is the idea:
The existing MTL is broken (see #1210), instead of letting this problem (and the solution of it) leak out to other cats typeclasses, we move them to a different library (mainly to support "entirely-separate law testing").
We release cats 1.0.0 after this new library is ready to use in a way by and large compatible with the existing MTL usage, i.e. users will have an easy migration path - simply add one more dependency.
Also from @edmundnoble (please feel free to edit/replace what you want to present here)
@edmundnoble and @adelbertc can you guys list other alternatives (e.g. adopting Scato)?
@typelevel/cats please provide your vote and/or feedback on this solution.
Update:
A summary of the feedbacks expressed so far:
MonadReader
,MonadWriter
,MonadState
.FunctorFilter
,TraverseFilter
andMonadFilter
, it seems everyone is okay for them to go too.MonadCombine
it's mostly either neutral or keeping it in cats, i.e. no strong preference of moving them outMonadError
we still have some disagreement.Now that we've done quite some debate, how about we resort to democracy for a resolution?
The text was updated successfully, but these errors were encountered: