-
Notifications
You must be signed in to change notification settings - Fork 520
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
Embed cats.syntax.all._
into IO
.
#3532
Conversation
Wow. Lol. This is quite a lot. I think it's extremely worth doing though. The unfortunate tradeoff is it significantly pollutes the |
It might be worth the effort to go through the imported syntax and ruthlessly drop any distractions - as an example, I do not find any meaningful extensions in |
FYI |
I went method by method through the proposed extensions. My belief is that we should not go the (easiest) generic way - the majority of the methods is (arguably) not needed at all and only adds polution to the namespace. Additionally, the To make the discussion concrete, take a look at the tables below. MonadErrorSyntax
FoldableSyntax
UnorderedFoldableSyntax
TraverseSyntax
UnorderedTraverseSyntax
TraverseFilterSyntax
TupleParallelSyntax
ParallelSyntax
ParallelFoldMapASyntax
ParallelFlatSyntax
ParallelTraverseSyntax
ParallelUnorderedTraverseSyntax
ParallelTraverseFilterSyntax
cats.effect.syntax.AllSyntax - done (all methods are already in IO) |
Hi all, I hope I'm not misunderstanding what the PR does, but I looked at the tests. Here's my 2 cents ... I think this adds a lot of unnatural garbage to IO's API, also generating confusion. Because people that expect this syntax also know to import cats.syntax.all (and one obvious question here is if there's any possibility for conflicts).
This syntax isn't natural for Scala. We only live with it, for the most part, because there are no better options for generic code, like a syntax meant for applicatives. And the problem is that it's not easy to discover. And the IDE has to struggle to suggests those methods regardless of where they comes from. If IntelliJ IDEA can do it, that's only a testament to how smart it is, given the implicit conversions going on for those ops. But when working with IO directly, it isn't generic, which is good, as it removes confusion in some cases. And the natural syntax for both of those cases is this one:
I.e., functions operating on more that 1 object go in the companion object (static methods in Java). That's where people look. This syntax also has prior art, including Future.sequence. And personally I'd really like |
@alexandru let me try to clarify. The main goal is to fulfill the following statement: "average user shall not need to import any magic implicits for typical IO operations". Personally, I am willing to die on this hill ;) Magical extensions through imports are really bad:
This migration away from the imported implicits has already started - It is my belief that enabling the following: (IO(1), IO(2)).parMapN(...)
(IO(1), IO(2)).parTupled
List(IO(1), IO(1)).sequence
List(IO(1), IO(1)).parSequence will be of big benefit for every user. Agreed, we shall add the To sum up - add a carefully chosen list of extension methods, with simplified signatures and a menaningful scaladoc. |
@kamilkloch Your tables are amazing thank you! @alexandru Overall, I see the point of this PR as "improve ergonomics". I don't want to create a lot of redundant ways of doing the same thing, ideally. Instead, I'd like to first reduce the friction and improve the discoverability of the approaches we already have. There are kind of three layers to this, starting from least to most disruptive:
Category 3 is where you start creating redundancy, and I'm very leery about it. Let's do the first two first and then decide if we still really want the third one. |
If users find about This syntax isn't discoverable by typing So, what category of users is this helping? UPDATE: and I think we should also worry about the performance of the compiler. Finding and sorting out those implicits isn't easy. What if all monadic types in the Typelevel ecosystem did this? |
BTW, Scala 3 is also smart enough to suggest an import, it seems:
|
No :), and that is a fact. Number one recurring question is the missing import.
The syntax will be perfectably discoverable when typing
Almost all users, in almost all use-cases. Please take a look at the tables above and see for yourserlf.
I do not get it - what kind of perforemance overhead, if we add a handful of methods directly to |
If that indeed happens, and I'd love to see some data on it, then it misses the forest from the trees, as the problem is the … freakishly odd syntax. Which also appears to be solvable via smarter tooling, like the Scala 3 compiler, or IntelliJ IDEA.
How do people know to construct a tuple when operating on 2 I don't think I've ever seen this in any other ecosystem. The only infamous example that comes close is
People working in the Typelevel ecosystem won't get rid of Duplicating the extension methods, that work via implicit conversions & higher-kinded types, for the entire functor hierarchy, plus Traverse/Parallel, doesn't seem light at all, sorry if I'm misreading this: with cats.syntax.MonadErrorSyntax
with cats.syntax.FoldableSyntax
with cats.syntax.UnorderedFoldableSyntax
with cats.syntax.TraverseSyntax
with cats.syntax.UnorderedTraverseSyntax
with cats.syntax.TraverseFilterSyntax
with cats.syntax.ParallelSyntax
with cats.syntax.ParallelFoldMapASyntax
with cats.syntax.ParallelFlatSyntax
with cats.syntax.ParallelTraverseSyntax
with cats.syntax.ParallelUnorderedTraverseSyntax
with cats.syntax.ParallelTraverseFilterSyntax
with cats.effect.syntax.AllSyntax |
Let me update the code and we shall talk it over. |
PS: I've said my 2 cents, and I don't think I can add more 🙂 so I'll stop now 😜 Sorry for bikeshedding, keep up the good work. |
@kamilkloch Also, I agree that the work you've put in that table is wonderful 🙂 |
Quick note: I'm pretty sure that this technique will have essentially zero impact on compile times. Happy to be proven wrong, but the compiler fixed implicit search performance in these types of cases a long time ago. The imports are far worse. Regarding the weird syntax, I agree to an extent, but there are only bad answers here if we want to add additional forms of the same thing. |
5a96cf7
to
30505be
Compare
cats.syntax.all._
into IO
.
tests/shared/src/test/scala-2.13+/cats/effect/IOImplicitSpec.scala
Outdated
Show resolved
Hide resolved
Okay bit of bikeshedding here… I'm still on the fence about @kamilkloch Amazing work on this. IMO it's ready to go aside from this last bit of quibbling. |
For Also, EDIT: |
Reuse existing syntax Update TestIOOps. Revert back to cherry-picked implementation, add missing methods. Add missing header. Add missing implementation. Remove unused import. Revert to Scala 2 import syntax. Reuse existing extension classes. Remove IO.parReplicateA, IOparReplicateA_. Add tests for adaptError and attemptTap. Remove TestIOOps. Cleanup. scalafmt. Add tests. Update tests. Fix type constraints. Add IO.traverse and IO.traverse_. Reorganize tests.
5dee140
to
7cc6d71
Compare
Thanks for sticking with this through the whole saga, @kamilkloch! I think this'll be a great set of changes. |
Work on #3531.