typelevel / toolkit

Quickstart your next app with the Typelevel Toolkit!
https://typelevel.org/toolkit
Apache License 2.0
94 stars 9 forks source link

Kitchen Sink import #2

Open armanbilge opened 1 year ago

armanbilge commented 1 year ago

A common complaint is about the enormous amount of imports it takes to do stuff. e.g. https://github.com/http4s/http4s/discussions/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!

zetashift commented 1 year ago

Somewhat hampered by: https://github.com/scalameta/metals/issues/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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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

zetashift commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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