From 6e78656e18c4dc4352012582caf715aa88b71d61 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Sun, 7 Mar 2021 20:54:19 +0100 Subject: [PATCH 1/2] Experiment: refine macro in Scala 3 This works with inline type classes and conditions that can be evaluated at compile-time like `>`, `!`, and `==`: ```scala scala> refineMV[Int, Positive1](5) val res1: eu.timepit.refined.api.Refined[Int, eu.timepit.refined.Positive1] = 5 scala> refineMV[Int, Positive1](-5) 1 |refineMV[Int, Positive1](-5) |^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |no scala> refineMV[String, NonEmpty1]("hello") val res2: eu.timepit.refined.api.Refined[String, eu.timepit.refined.NonEmpty1] = hello scala> refineMV[String, NonEmpty1]("") 1 |refineMV[String, NonEmpty1]("") |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |no ``` --- .../eu/timepit/refined/package.scala | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/package.scala b/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/package.scala index f8f8187fe..e043c867f 100644 --- a/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/package.scala +++ b/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/package.scala @@ -14,6 +14,28 @@ package object refined { */ def refineV[P]: RefinePartiallyApplied[Refined, P] = RefType.refinedRefType.refine[P] + trait Predicate[T, P] { + inline def isValid(inline t: T): Boolean + } + + class Positive1 + object Positive1 { + inline given Predicate[Int, Positive1] with + inline def isValid(inline t: Int): Boolean = t > 0 + } + + class NonEmpty1 + object NonEmpty1 { + inline given Predicate[String, NonEmpty1] with + inline def isValid(inline s: String): Boolean = !(s == "") + // If we use !s.isEmpty here, we get the following compile error: + // |Cannot reduce `inline if` because its condition is not a constant value: "hello".isEmpty().unary_! :Boolean + } + + inline def refineMV[T, P](inline t: T)(using inline p: Predicate[T, P]): Refined[T, P] = { + inline if (p.isValid(t)) Refined.unsafeApply(t) else scala.compiletime.error("no") + } + /** * Alias for `[[api.RefType.refine]][P]` with `shapeless.tag.@@` as type * parameter for `[[api.RefType]]`. From 741c966b52e69fcf274af0e2fcc6760cfd94897a Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Fri, 16 Jul 2021 08:44:05 +0200 Subject: [PATCH 2/2] Greater1 predicate --- .../eu/timepit/refined/package.scala | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/package.scala b/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/package.scala index e043c867f..73c477073 100644 --- a/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/package.scala +++ b/modules/core/shared/src/main/scala-3.0+/eu/timepit/refined/package.scala @@ -24,6 +24,26 @@ package object refined { inline def isValid(inline t: Int): Boolean = t > 0 } + class Greater1[N] + object Greater1 { + inline given [N <: Int]: Predicate[Int, Greater1[N]] with + inline def isValid(inline t: Int): Boolean = t > scala.compiletime.constValue[N] + } +/* + Does not work with 3.0.0: + scala> refineMV[Int, Greater1[2]](5) +1 |refineMV[Int, Greater1[2]](5) + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |Cannot reduce `inline if` because its condition is not a constant value: { + | val given_Predicate_Int_Greater1_this: + | eu.timepit.refined.Greater1.given_Predicate_Int_Greater1[(2 : Int)] + | = eu.timepit.refined.Greater1.given_Predicate_Int_Greater1[(2 : Int)] + | true:Boolean + |} + | This location contains code that was inlined from package.scala:42 + */ + + class NonEmpty1 object NonEmpty1 { inline given Predicate[String, NonEmpty1] with