Skip to content
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

Kitchen Sink import #2

Open
armanbilge opened this issue Feb 24, 2023 · 17 comments
Open

Kitchen Sink import #2

armanbilge opened this issue Feb 24, 2023 · 17 comments
Labels
question Further information is requested

Comments

@armanbilge
Copy link
Member

A common complaint is about the enormous amount of imports it takes to do stuff. e.g. http4s/http4s#6696

What if ... we just exported a bunch of useful stuff under a single package?

package typelevel.toolkit

export cats.*
export cats.effect.*
export fs2.*
export org.http4s.*
export org.http4s.ember.client.EmberClientBuilder
// etc.

Not gonna lie, it's playing with fire a bit. Probably instead of wildcards we should be thoughtful about the specific things we export.

But then:

import typelevel.toolkit.*

and you are off to the races!

@armanbilge armanbilge added the question Further information is requested label Feb 24, 2023
@zetashift
Copy link
Contributor

Somewhat hampered by: scalameta/metals#4325

It's going to be hard to strike a balance here, and arguably some of the import complaints should be resolved by the libraries themselves rather than toolkit providing a way out(http4s :P).

One aspect of the import complaints is that, usually the answer is: import cats.syntax.all.* or some variation of that. I think if the "kitchen sink" import only solved that aspect, that'd be an efficient start.

@TonioGela
Copy link
Member

One aspect of the import complaints is that, usually the answer is: import cats.syntax.all.* or some variation of that. I think if the "kitchen sink" import only solved that aspect, that'd be an efficient start.

IMHO a good fit might be this 👇

package typelevel.toolkit

export cats.syntax.all.*
export cats.effect.*
export fs2.*

but I'll confirm what I'm saying after I'll have the time to enucleate all the stuff that will get imported in this way.
WDYT @zetashift ?

@zetashift
Copy link
Contributor

One aspect of the import complaints is that, usually the answer is: import cats.syntax.all.* or some variation of that. I think if the "kitchen sink" import only solved that aspect, that'd be an efficient start.

IMHO a good fit might be this point_down

package typelevel.toolkit

export cats.syntax.all.*
export cats.effect.*
export fs2.*

but I'll confirm what I'm saying after I'll have the time to enucleate all the stuff that will get imported in this way. WDYT @zetashift ?

I think I'm going to use enucleate in daily life :P.

In all seriousness, that looks good to me!

@Koroeskohr
Copy link

my only issue is that with export fs2.* we completely shadow io.circe because of fs2.io. _root_ is a workaround but that prevents discoverability and is detrimental to a first experience with the toolkit

@TonioGela
Copy link
Member

my only issue is that with export fs2.* we completely shadow io.circe because of fs2.io. _root_ is a workaround but that prevents discoverability and is detrimental to a first experience with the toolkit

That's totally true, but (thinking out loud) isn't that solvable adding export io.circe.*? AFAIK every circe module lives under io.circe.

@armanbilge
Copy link
Member Author

Note that Decoder, Encoder, and Codec (as in io.circe.*, skunk.*, scodec.*) are very overloaded. In theory we could remap these a la JsonDecoder etc. but there's this overarching concern that you might know how to do things with the toolkit, but take away the training wheels and you don't know what anything is actually called or where it comes from.

@armanbilge
Copy link
Member Author

my only issue is that with export fs2.*

I think the answer here is, don't do that :) instead of a wildcard we should export very specific things, like Stream.

@TonioGela
Copy link
Member

Discussing with @armanbilge, we came up with the idea that a Kitchen Sink import is probably not a great idea because it can be confusing for beginners progressing past the "hello world" with the TL stack.

Code written relying on this kitchen sink import won't be copy-pastable to other codebases as imports may need to be added. If this may not represent a problem for users proficient with the toolkit's libraries, beginners may struggle to compile the code as it may even happen that the IDE won't cooperate.

Last but not least, at the import level, the examples in the toolkit homepage will be different from those in every included repository.

We're trying to achieve higher ergonomics at the cost of clarity/compatibility. It may not be the correct way or place to solve the "lots of imports" problem.

WDYT?

@zetashift
Copy link
Contributor

Discussing with @armanbilge, we came up with the idea that a Kitchen Sink import is probably not a great idea because it can be confusing for beginners progressing past the "hello world" with the TL stack.

Since the import is opt-in, I think it's okay if we entertain the idea. But reading the downsides you listed, it's a net-negative for me. Especially the copy-pastability of scripts hurts most :(.

@armanbilge
Copy link
Member Author

I think the best long-term strategy is that we should keep pursuing ways to reduce the number of imports necessary overall in the various libraries. At present we are in a really annoying spot, but somewhere in the future we can define syntax directly on typeclasses, so that the syntax imports are no longer necessary.

@armanbilge
Copy link
Member Author

we should keep pursuing ways to reduce the number of imports necessary overall

Here's a contributors thread by @benhutchison on this topic!

What’s the problem? Modern Scala code written on top of library-ecosystems such as Typelevel is dependent on a huge number of imports to work correctly. The import section reaches 50 lines long in some of my application files, which rivals the actual application code.

https://contributors.scala-lang.org/t/idiomatic-imports/5788/1

@armanbilge
Copy link
Member Author

I was also reminded of this.
https://gist.github.com/Daenyth/03889157b1c409196c17d8d8e509e603

@zetashift
Copy link
Contributor

zetashift commented Mar 13, 2023

https://contributors.scala-lang.org/t/idiomatic-imports/5788/1

Yea the import problem and discovery of things is really mehh. But reading through the thread it seems a more powerful export is also necessary?
Until then, I hope we can pick up some low-hanging fruit fixes for typelevel libraries, because when I look at http4s or circe, I have the feeling that some things can be easier wrt imports, at the library level.

@TonioGela
Copy link
Member

https://contributors.scala-lang.org/t/idiomatic-imports/5788/1

TBH I think Martin has a point in saying "you are establishing a DSL that only an expert can be comfortable in" while talking about -Yimports:.

Also, to cite Ben:

Ensuring imports are consistent across files is a source of errors & low-value busy-work.
Changes to import lines clog up diffs and distract from more interesting changes.
Verbose imports add friction to refactors and experiments.

IMHO, the consistency across files is probably the most time-consuming thing that happens, as nowadays Metals and IntelliJ do a pretty good job at importing stuff in a specific file. As this toolkit was "realized with Scala CLI in mind" we can assume (but not pretend ofc) that a good number of uses will be for single-file applications, solving the consistency problem at its root.

What Daenyth did in the CE prelude was the scala 3 export alternative that I had in mind while looking at this issue. I'm on the same page as @zetashift: I think that overall re-namespacing the most used classes/data structures/syntaxes may have a net-negative impact.

@benhutchison
Copy link
Member

IMHO, the consistency across files is probably the most time-consuming thing that happens, as nowadays Metals and IntelliJ do a pretty good job at importing stuff in a specific file

Actually, my experience is that IDE automation falls far short my ideal wish-list around imports. And this is in large part because the problem is hard.

Example. If you didn't already have import cats.syntax.all.* in a file, but did have Cats on your classpath, and you typed "option".some, AFAIK no IDE is smart enough to tell you, hey if you import this package you can enable that method."

Similarly, if you activate code completion, you will not see extension method options unless they were already imported. But often the biggest challenge is discovering what extensions exist and how to import them.

Finally, neither IDE is yet smart enough to make decisions about whether added Scala 3 imports should be .*, .given or .{*, given}.

@zetashift
Copy link
Contributor

As a small update, with the recent improvements of toolkit and typelevel being a kit, I don't have anything else to complain(things are nice! :P) about except for the imports issue across Typelevel ecosystem.

A Prelude doesn't seem so bad these days, but it's a very conflicting feel.

@TonioGela
Copy link
Member

A Prelude doesn't seem so bad these days, but it's a very conflicting feel.

I thought so many times to publish a toolkit + a Prelude under my group id just to test out if this solution works for my day by day 🤔 I'll do it eventually

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

5 participants