From 98a4a4d1afaffe9d2c49544b1aa7b8c50fb2e561 Mon Sep 17 00:00:00 2001 From: Kyri Petrou Date: Sun, 9 Jul 2023 12:22:41 +0200 Subject: [PATCH 1/2] Use non-boxing `forall` strings --- .../iltotore/iron/constraint/collection.scala | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/main/src/io/github/iltotore/iron/constraint/collection.scala b/main/src/io/github/iltotore/iron/constraint/collection.scala index 2bc58a42..374f838a 100644 --- a/main/src/io/github/iltotore/iron/constraint/collection.scala +++ b/main/src/io/github/iltotore/iron/constraint/collection.scala @@ -169,7 +169,23 @@ object collection: .map(applyConstraint(_, constraintExpr)) .foldLeft(Expr(true))((e, t) => '{ $e && $t }) - case None => '{ $expr.forall(c => ${ applyConstraint('c, constraintExpr) }) } + case None => '{ forallOptimized($expr, (c: Char) => ${ applyConstraint('c, constraintExpr) }) } + + /** + * Scala's [[Function1]] doesn't have a specialization on [[Char]] arguments, which causes each char in the string to be boxed + * when calling `forall`. This trait is used as a substitute to avoid this issue. + */ + private trait EvalChar: + def apply(value: Char): Boolean + + private def forallOptimized(s: String, p: EvalChar): Boolean = + var i = 0 + val len = s.length + while i < len + do + if !p(s.charAt(i)) then return false + i += 1 + true given [C1, C2](using C1 ==> C2): (ForAll[C1] ==> Exists[C2]) = Implication() given [C1, C2](using C1 ==> C2): (ForAll[C1] ==> Last[C2]) = Implication() From b73823aefd3f2c6c6a07f15a3a6146eabc5492f5 Mon Sep 17 00:00:00 2001 From: Kyri Petrou Date: Sun, 9 Jul 2023 12:44:59 +0200 Subject: [PATCH 2/2] Implement optimized `exists` --- .../iltotore/iron/constraint/collection.scala | 48 +++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/main/src/io/github/iltotore/iron/constraint/collection.scala b/main/src/io/github/iltotore/iron/constraint/collection.scala index 374f838a..3b9d2ab4 100644 --- a/main/src/io/github/iltotore/iron/constraint/collection.scala +++ b/main/src/io/github/iltotore/iron/constraint/collection.scala @@ -169,23 +169,7 @@ object collection: .map(applyConstraint(_, constraintExpr)) .foldLeft(Expr(true))((e, t) => '{ $e && $t }) - case None => '{ forallOptimized($expr, (c: Char) => ${ applyConstraint('c, constraintExpr) }) } - - /** - * Scala's [[Function1]] doesn't have a specialization on [[Char]] arguments, which causes each char in the string to be boxed - * when calling `forall`. This trait is used as a substitute to avoid this issue. - */ - private trait EvalChar: - def apply(value: Char): Boolean - - private def forallOptimized(s: String, p: EvalChar): Boolean = - var i = 0 - val len = s.length - while i < len - do - if !p(s.charAt(i)) then return false - i += 1 - true + case None => '{ $expr.forallOptimized(c => ${ applyConstraint('c, constraintExpr) }) } given [C1, C2](using C1 ==> C2): (ForAll[C1] ==> Exists[C2]) = Implication() given [C1, C2](using C1 ==> C2): (ForAll[C1] ==> Last[C2]) = Implication() @@ -223,7 +207,7 @@ object collection: .map(applyConstraint(_, constraintExpr)) .foldLeft(Expr(true))((e, t) => '{ $e && $t }) - case None => '{ $expr.init.forall(c => ${ applyConstraint('c, constraintExpr) }) } + case None => '{ $expr.init.forallOptimized(c => ${ applyConstraint('c, constraintExpr) }) } given [C1, C2](using C1 ==> C2): (Init[C1] ==> Exists[C2]) = Implication() @@ -260,7 +244,7 @@ object collection: .map(applyConstraint(_, constraintExpr)) .foldLeft(Expr(true))((e, t) => '{ $e && $t }) - case None => '{ $expr.tail.forall(c => ${ applyConstraint('c, constraintExpr) }) } + case None => '{ $expr.tail.forallOptimized(c => ${ applyConstraint('c, constraintExpr) }) } given [C1, C2](using C1 ==> C2): (Tail[C1] ==> Exists[C2]) = Implication() given [C1, C2](using C1 ==> C2): (Tail[C1] ==> Last[C2]) = Implication() @@ -295,7 +279,7 @@ object collection: .map(applyConstraint(_, constraintExpr)) .foldLeft(Expr(false))((e, t) => '{ $e || $t }) - case None => '{ $expr.exists(c => ${ applyConstraint('c, constraintExpr) }) } + case None => '{ $expr.existsOptimized(c => ${ applyConstraint('c, constraintExpr) }) } object Head: @@ -354,3 +338,27 @@ object collection: case None => '{ $expr.lastOption.exists(last => ${ applyConstraint('{ last }, constraintExpr) }) } given [C1, C2](using C1 ==> C2): (Last[C1] ==> Exists[C2]) = Implication() + + /** + * Scala's [[Function1]] doesn't have a specialization on [[Char]] arguments, which causes each char in the string to be boxed + * when calling `forall`. This trait is used as a substitute to avoid this issue. + */ + private trait EvalChar: + def apply(value: Char): Boolean + + extension (s: String) + private def forallOptimized(p: EvalChar): Boolean = + var i = 0 + val len = s.length + while i < len do + if !p(s.charAt(i)) then return false + i += 1 + true + + private def existsOptimized(p: EvalChar): Boolean = + var i = 0 + val len = s.length + while i < len do + if p(s.charAt(i)) then return true + i += 1 + false