TomasMikula / libretto

Declarative concurrency and stream processing library for Scala
Mozilla Public License 2.0
199 stars 7 forks source link

Rethink how given instances are exposed #86

Open MateuszKowalewski opened 1 year ago

MateuszKowalewski commented 1 year ago

The move to given made random looking "given imports" in random looking places necessary.

This looks fishy.

I guess some redesign of how the givens, exports, and imports are structured would be good. Something like the common "syntax" pattern (similar to e.g. Cats) could help here.

TomasMikula commented 1 year ago

I think the description needs to be a bit more concrete.

MateuszKowalewski commented 1 year ago

I'm still digesting how the DSL and it's implementation are "folded".

You seem to practice some form of "origami" with the different parts of the DSL and it's implementation. (I'm even not sure this is a good idea in general, but that's something for another issues.)

I've just noticed that this isn't very straight forward, and after replacing implicits with givens we have even some imho random looking imports here and there: Remember the comments about "why do we need this import here"? (Which I removed to open this issue here.)

I think this here is a long term issue. Nothing that needs instant action. But something to keep on the screen.

As soon as I understand more about the code and it's structure I'll come up likely with some proposal how to proceed here. But maybe you have already some ideas how to improve things for end-users? (Especially more documentation would be a really helpful first step… :wink: Concrete questions would be: How the DSLs, Libs, and Kits are meant to be used? What it the canonical way to start a Libretto app? Yes, one can figure it out by looking at the code. But "normal", future end-users should have docu for that.)

TomasMikula commented 1 year ago

I meant the description of the problem needs to be more concrete. You don't have to have an idea of the solution in mind.

Remember the comments about "why do we need this import here"?

I do remember that for now, and know where to go looking for that random looking code. I will not know that in a few months. All I mean is to make the description of the problem more self-contained ;)

How the DSLs, Libs, and Kits are meant to be used?

DSLs

Libretto is not one language, that would be too easy 😄 There is a hierarchy of languages. For example, there is CoreDSL. Then there is Scaletto, which extends CoreDSL.

Scaletto gives more power to the users (producers of programs), namely gives them the ability to use Scala functions and Scala objects as managed resources.

CoreDSL gives more power to the interpreters (consumers of programs). For example, absence of Scala functions makes it possible to serialize, analyze, generate code from, visualize, ... CoreDSL programs.

The point being, "constraints liberate, liberties constrain."

Whenever you write a piece of Libretto code, you have to pick a DSL you are writing it in. You would pick the least powerful DSL that has all you need.

Libs

A lib is a library of code (functions, type definitions) built on top of a DSL.

If a lib requires only CoreDSL, everything in it can be used in a Scaletto program as well.

OTOH, things from a lib defined on top Scaletto cannot be used from a CoreDSL program.

Kits

A kit is meant to shield the end user from the above complexity.

A kit picks one DSL, one runtime, and perhaps a bunch of libs, and makes them available to the user, without the user having to "fold the origami" themselves. Or at least that's the goal.

User libs

What if users want to write their own libraries?

They can start by writing it against (a DSL from) a selected kit.

Later, they may make it polymorphic in the DSL and runtime, by requiring only (any implementation of) the least powerful DSL.

What it the canonical way to start a Libretto app?

Write the app against a concrete kit. There's really only one kit at the moment, StarterKit. It implements Scaletto. For the main executable, extend StarterApp and implement it's blueprint method.

object MyApp extends StarterApp {
  override def blueprint: Done -⚬ Done =
    /* your code goes here */
}