softwaremill / magnolia

Easy, fast, transparent generic derivation of typeclass instances
https://softwaremill.com/open-source/
Apache License 2.0
760 stars 116 forks source link

Missing features for Scala 3 #296

Open mbore opened 3 years ago

mbore commented 3 years ago
KacperFKorban commented 3 years ago

Hi, could You elaborate on support for recursive types and accessing annotations?

mbore commented 3 years ago

Hi, sure:

KacperFKorban commented 3 years ago

Hi, recursive types are indeed supported. See tests for Tree here. Your snippet with a small tweak works:

sealed trait Node
case class Edge(id: Long, source: Node) extends Node
case class SimpleNode(id: Long) extends Node

object Node:
  given Show[String, Node] = Show.derived

test("rerucsive class") {
  val res = summon[Show[String, Node]].show(Edge(1, SimpleNode(2)))
  println(res)
}

In order for recursive structures to work with magnolia for Scala 3, an explicit given is needed. Fortunately for enums and type classes with single parameters, it can be neatly expressed with just derives clauses. Just like in the readme. So using enums is the preferred way of defining those structures in Scala 3. I think that it's more of a documentation issue. I'll try to make it more explicit in readme.

adamw commented 3 years ago

Re: recursive types - I think we also need to have them supported if the given is defined externally, not on the companion object. This is needed so that we can work with externally defined types.

E.g. I think typeclasses for List should be derivable given given [T] Show[T] = Show.derived

KacperFKorban commented 3 years ago

RE: a simple way to pass configuration - since Scala 3 adds Trait Parameters wouldn't it be enough to just add a configuration parameter to the derivation trait?

joroKr21 commented 3 years ago

RE: a simple way to pass configuration - since Scala 3 adds Trait Parameters wouldn't it be enough to just add a configuration parameter to the derivation trait?

That could work, or it could be a method that you can override (depending on ergonomics). It would have to be inline. Edit: In fact it would have to be a method, because trait/class parameters cannot be inline.

adamw commented 3 years ago

@joroKr21 @KacperFKorban Maybe I'm misunderstanding your proposal, but once the companion object extends the Derivation trait, the parameters need to be fixed. Here, we are looking at parametrising the derivation for specific .derived invocations.

I think we wanted to try a two-level solution, where the .derived method is available via an extension which requires the implicit configuration; the extension then implements the Derivation trait and has access to the parameter. But not sure if it's done in tapir, @mbore ?

joroKr21 commented 3 years ago

Maybe I'm misunderstanding your proposal, but once the companion object extends the Derivation trait, the parameters need to be fixed.

Yeah I thought that's what we wanted (to configure the derivation itself).

I think we wanted to try a two-level solution, where the .derived method is available via an extension which requires the implicit configuration; the extension then implements the Derivation trait and has access to the parameter.

So you mean passing runtime configuration, not compile-time? I guess that is another axis of configuration but it would have to be customisable.

adamw commented 3 years ago

So you mean passing runtime configuration, not compile-time? I guess that is another axis of configuration but it would have to be customisable.

Yes, e.g. in tapir when deriving the Schema, we pass in configuration which specifies how field names should be transformed - this can be from camelCase to snake_case etc.

mbore commented 3 years ago

I think we wanted to try a two-level solution, where the .derived method is available via an extension which requires the implicit configuration; the extension then implements the Derivation trait and has access to the parameter. But not sure if it's done in tapir, @mbore ?

Yes, you right, but we haven't implemented it yet.

kpodsiad commented 2 years ago

Can someone elaborate about support for values classes? What differs from Scala 2, what needs to done to support it, etc.

KacperFKorban commented 2 years ago

The problem is that value classes don't have generated Mirrors in Scala 3. I'm not sure if there is anything to be done here. If I remember correctly, the problem was that one of the generated methods conflicted after erasure.

The most obvious fix would be finishing #321, though I'm not sure how to tackle some of the new problems posted there.

kpodsiad commented 2 years ago

Thanks for answer @KacperFKorban.

Is mirror-less approach needed? I wonder if something similar to this can be done here. It's a hybrid approach that piggybacks on mirrors when they can be generated and provides necessary information for other types for which there are no mirrors, like value classes.

adamw commented 1 year ago

@kpodsiad that could probably work :) maybe you'd like to try implementing this in magnolia?

adamw commented 1 year ago

Partial support for value classes (case classes, without type parameters) has been added in https://github.com/softwaremill/magnolia/pull/435