scalalandio / chimney

Scala library for boilerplate-free, type-safe data transformations

Home Page:https://chimney.readthedocs.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Transform into singleton types

MateuszKubuszok opened this issue · comments

Currently the only kinds support for singleton types we have are:

  • case objects (Scala 2 & 3) and parametherless enum cases (Scala 3)
  • filling Unit as constructor parameter/setter
  • filling None as constructor parameter/setter if we enable it with a flag enableOptionDefaultsToNone

We could allow transformting into/filling automatically any literal singleton types (e.g. Strings, Ints, etc literal types), since there is only 1 possible value that can be filled and we know exactly what's that value is.

Implementation would require:

  • creating new datatype in chimney-macro-commons
  • implementing macros (2.12/2.13/3) which would check that a type is singleton type
  • implementing new rule
  • testing
  • checking that this new rule is not colliding with existing rules (e.g. we explicitly ignore None in some methods, so perhaps we should ignore some singleton types or only support primitives literals: Unit/String/Int/numerical types)

Transforming enums into underlying singletons i.e. via getValue could be extremely convenient, wondering are there any easy solutions to that right now?

I think something like that is cooked up by https://github.com/moia-oss/teleproto for PB enums.

I'd say that some particular cases could be user-extended by providing the right generic implicit. On Scala 3 that could be something with a Mirror I guess?

// maybe it would work, haven't checked
given unwrapSingleton[A <: Product, B](
  using Mirror.ProductOf[A] { type MirroredElemLabels = B *: EmptyTuple }
): Transformer[A, B] =
  (src: A) => src.productIterator.head.asInstanceOf[B]

given wrapSingleton[A <: Product, B](
  using m: Mirror.ProductOf[A] { type MirroredElemLabels = B *: EmptyTuple }
): Transformer[B, A] =
  (src: B) => m.fromTuple(src *: EmptyTuple)

If your enum extends some particular interface it can probably use it to extract the inner values.


Technically, macro are not far from being able to always unwrap/wrap values - there are rules that handle AnyVals for wrapping/unwrapping/rewrapping where we would only have to drop AnyVal check, but it haven't been done since:

  • it would be a change of behavior that nobody requested before
  • if any 1-value product is treated the same way as AnyVal I can imagine that in some cases like A1(B1(C1(D1(...)))) into A2(B2(C2(D2(...)))) testing every possibility (nesting or not) might explode the compilation time

so I was waiting till some sane way of saying what can be unwrapped besides AnyVals can be invented.

@MateuszKubuszok Oh that's right! Thanks for pointers. I'll try to play around for the local solution, nice.