scalaimplicit-conversionscala-3refined

Int Refined Positive doesn't compile


The followfing code doesn't compile:

import eu.timepit.refined._
import eu.timepit.refined.api.Refined
import eu.timepit.refined.auto._
import eu.timepit.refined.numeric._

val i1: Int Refined Positive = 5

The error is:

[error] Found:    (5 : Int)
[error] Required: Int Refined eu.timepit.refined.numeric.Positive
[error] val i1: Int Refined Positive = 5

I used Scala 3.2.2 and latest Refined eu.timepit::refined:0.10.2

According to documentation https://github.com/fthomas/refined it shoud compile.


Solution

  • Refined implicit conversions eu.timepit.refined.auto._ are macro-based (Scala 2 macros):

    https://github.com/fthomas/refined/blob/v0.10.2/modules/core/shared/src/main/scala-3.0-/eu/timepit/refined/auto.scala#L58-L68 (Scala 2)

    https://github.com/fthomas/refined/blob/v0.10.2/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/auto.scala (Scala 3)

    // Scala 2
    implicit def autoRefineV[T, P](t: T)(implicit
      rt: RefType[Refined],
      v: Validate[T, P]
    ): Refined[T, P] = macro RefineMacro.impl[Refined, T, P]
    

    Scala 2 macros do not expand in Scala 3

    refined macros not yet available

    https://scalacenter.github.io/scala-3-migration-guide/docs/macros/macro-libraries.html

    So the code you provided compiles in Scala 2: https://scastie.scala-lang.org/DmytroMitin/aj78AodyQkK1b9F66RXTsA/3

    but not in Scala 3: https://scastie.scala-lang.org/DmytroMitin/aj78AodyQkK1b9F66RXTsA/1

    See the ticket Macros missing for Scala 3 https://github.com/fthomas/refined/issues/932

    The issue is that Refined Scala 2 macros use context.eval. What c.eval does is transforming (compiling, evaluating) an abstract syntax tree (AST) into the value of this tree:

    Scala: what can code in Context.eval reference?

    Def Macro, pass parameter from a value

    Scala: how to force converting a statement to literal?

    Compile-time c.eval used in macros is similar to runtime toolbox.eval used in runtime compilation

    How can I run generated code during script runtime? (Scala 2)

    How to compile and execute scala code at run-time in Scala3? (Scala 3)

    c.eval is absent in Scala 3. More precisely, in Scala 3 there is staging.run similar to tb.eval but it's forbidden in Scala 3 macros because this would violate the phase consistency principle (PCP). Indeed, c.eval/tb.eval/staging.run transforms a tree (a value from a previous stage) into the value of this tree (a value from a next stage).

    Sometimes c.eval can be emulated in Scala 3 (but starting from a source code rather than tree): get annotations from class in scala 3 macros

    In order to use actual c.eval/tb.eval/staging.run (starting from a tree) in Scala 3 macros, one would need to patch Scala 3 compiler: https://github.com/DmytroMitin/dotty-patched (or maybe a compiler plugin would be enough).


    Scala 2 macros: https://docs.scala-lang.org/overviews/macros/overview.html

    Scala 3 macros: https://docs.scala-lang.org/scala3/reference/metaprogramming/macros.html

    Scala 2 macros to Scala 3 macros migration guide: https://docs.scala-lang.org/scala3/guides/migration/compatibility-metaprogramming.html