I like the new transparent mechanism quite a lot, and can see its power. The only thing that seems to be missing, offhand, is something equivalent to Generic and LabelledGeneric â a way to convert between the various sorts of strongly-typed products. Are there plans to add a mechanism for that?
Quite possibly, but Iâm concerned that it may make applications structurally messy. Or possibly Iâm misunderstanding how these macro annotations would come into play.
I guess the question is, if I am writing an application that is depending New Circe (however that works in the new world), can I do my serialization without having to break things down into multiple projects? I donât mind things getting a little complex for libraries; Iâm more concerned if thatâs the case for routine applications.
Thatâs really the use case that Iâd like to see fully-worked in the new world â my observation is that many problems seem to be Circe-complete. (That is, they turn out to want essentially the same machinery as Circe.) So if a consumer of New Circe could operate with reasonably minimal boilerplate, Iâll believe that many use cases are solved. But so far, I donât quite grok how that would work in the new environment.
No. Generics are currently generated on the fly for any case class and are available in the same project where case class is defined. Limiting them only to dependent projects would kill the entire ecosystem of typeclass derivationâŚ
In that case, I think we would need one standard Generic-like instance generated by the Scala compiler for each case class, so that libraries like circe can leverage it via implicits. And transparent methods could simplify this process (reducing the amount of implicit definitions needed). Additionally, it would be really cool if transparent methods and/or implicits could be cached somehow, so we donât end up generating as much duplicated code as today.
Additionally, it would be really cool if transparent methods and/or implicits could be cached somehow, so we donât end up generating as much duplicated code as today.
Implicits cannot be cached because theyâre not coherent and not pure. Transparent methods are all that + dependent on call site scope.
We plan to put typeclass derivation in the language, by adding a scheme where typeclasses can be defined automatically for case class hierarchies. What transparent functions give us in this respect is that we can have a simple fold-based derivation mechanism and still get the full power of Generic and LabelledGeneric. At least I hope so - we still have to try that out.
@AMatveev It seems that something like what youâre asking for (run a macro once per class) could be achieved by typeclass derivation. But a macro like transparent def apply(x: T) is too powerful to be optimized like you ask: it must be expanded at each invocation (just like with current macros) because it can generate different code for each x.
I know, but I was asking about the difference between transparent and Scala 2 macros in this regard, which seems to be ânoneâ.
Iâm not sure how relevant this thread is anymore, but one more note:
I recently started to play with quill which is an awesome lib for DB access. It relies on quoted dsl to construct sql queries during compilation. I believe it wonât be possible without the whitebox macros. It would be very sad if such libs wonât be possible in scala 3.
Excuse me if it was discussed before, but I could find neither quill nor quoted DSL in this thread.
I believe it wonât be possible without the whitebox macros.
Can you explain why it wonât be possible without whitebox macros? quill is surely a good use case so itâs worth figuring out whatâs needed to support it.
Quill propagates refinement types along with their quoted SQL snippets in order to perform SQL construction & optimization at compile time. Greatly simplified, it basically takes
val x = quote{1}
val y = quote{x + 2}
val z = quote{y + 3}
And the quote refines the types being returned so the contents of each call is visible in the type as an annotation:
val x: Quote[Int] @ast("1") = quote{1}
val y: Quote[Int] @ast("1 + 2") = quote{x + 2}
val z: Quote[Int] @ast("1 + 2 + 3") = quote{y + 3}
And then proceeds to perform optimizations at compile-time based on the annotated AST:
val x: Quote[Int] @ast("1") = Quote(1)
val y: Quote[Int] @ast("1 + 2") = Quote(3)
val z: Quote[Int] @ast("1 + 2 + 3") = Quote(6)
Quill doesnât need all the power of whitebox macros. The annotations it uses to pass data around between invocations is âside channelâ-ish: they never affect the âprimaryâ type e.g. Quote[Int], but only the contents of downstream annotations.
Without the ability to pass annotations between calls via their types, Quill falls back to performing the optimisations and transformations at runtime. This works, but pushes computation-overhead and failure-reporting to runtime, whereas with the annotations present it can do all that and report any errors before you ever run any code. This would be the case if whitebox macros did not exist (blackbox macros cannot refine the types they return based on the AST captured).
Note that in the proposed new meta programming framework, we still can create new types after typing, itâs just that those types are unavailable for the main type checking itself. So refinement types could be created as a side channel during macro expansion.
However, thereâs another problem: We need to solve the problem of dependencies and separate compilation. Refinement types depend on other refinement types in fairly arbitrary ways. This has to be handled. A large part of the more sophisticated parts of Scalaâs typer are there to deal with this problem. It seems a later phase of macro expansion has to implement something similar.
It seems that itâd be possible to implement Quillâs compile-time query generation with inline. @odersky will the tree of the method marked with inline be visible if it is used as a parameter of a macro?
will the tree of the method marked with inline be visible if it is used as a parameter of a macro?
Yes. You can get at it using Tasty reflection. @nicolasstucki can give more details. He just presented a paper at the Scala Symposium that shows a key technique for doing this.