From e6e9406aeb2c6a905a2604ee76f44a1069142270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Kozak?= Date: Fri, 4 Sep 2026 14:24:37 +0200 Subject: [PATCH 1/3] less inlines --- commons/TupleOps.scala | 20 ++++++++++++-------- commons/containsOnly.scala | 28 +++++++++++++++------------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/commons/TupleOps.scala b/commons/TupleOps.scala index 05ab7f4..90052ba 100644 --- a/commons/TupleOps.scala +++ b/commons/TupleOps.scala @@ -6,17 +6,20 @@ import scala.compiletime.ops.int.S import scala.reflect.ClassTag extension (tup: Tuple) - inline def foreach(f: [t] => t => Unit): Unit = tup.map[[X] =>> Unit](f) + def foreach(f: [t] => t => Unit): Unit = tup.map[[X] =>> Unit](f) - inline def indices: Indices[tup.type] = Tuple.fromArray(Array.range(0, tup.size)).asInstanceOf[Indices[tup.type]] + def indices: Indices[tup.type] = Tuple.fromArray(Array.range(0, tup.size)).asInstanceOf[Indices[tup.type]] inline def hasDuplicates: HasDuplicates[tup.type] = compiletime.constValue[HasDuplicates[tup.type]] - inline def mapAs[T](using inline ev: tup.type containsOnly T)[F[_ <: T]](inline f: [t <: T] => t => F[t]) - : Tuple.Map[tup.type, [X] =>> F[X & T]] = + def mapAs[T](using ev: tup.type containsOnly T)[F[_ <: T]](f: [t <: T] => t => F[t]) + : Tuple.Map[tup.type, [X] =>> F[X & T]] = tup.map[[X] =>> F[X & T]]([t] => (t: t) => f(t.asInstanceOf[t & T])) - inline def toArrayOf[T](using inline ev: tup.type containsOnly T)(using ClassTag[T]): Array[T] = tup match + def toArrayOf[T](using ev: tup.type containsOnly T)(using ClassTag[T]): Array[T] = + toArrayOfImpl[T] + + private def toArrayOfImpl[T: ClassTag]: Array[T] = tup match case EmptyTuple => Array.empty[T] case self: Product => val arr = new Array[T](self.productArity) @@ -25,8 +28,9 @@ extension (tup: Tuple) arr(i) = self.productElement(i).asInstanceOf[T] i += 1 arr - - inline def to[T](using inline ev: tup.type containsOnly T)[C](factory: Factory[T, C]): C = + def to[T](using ev: tup.type containsOnly T)[C](factory: Factory[T, C]): C = + toImpl(factory) + def toImpl[T, C](factory: Factory[T, C]): C = factory.fromSpecific(tup.productIterator.asInstanceOf[Iterator[T]]) type Indices[Tup <: Tuple] <: Tuple = Tup match @@ -41,5 +45,5 @@ type HasDuplicates[Tup <: Tuple] <: Boolean = Tup match case EmptyTuple => false case h *: t => Tuple.Contains[t, h] || HasDuplicates[t] -inline def realCons(x: Any, tup: Tuple): x.type *: tup.type = +def realCons(x: Any, tup: Tuple): x.type *: tup.type = runtime.Tuples.cons(x, tup).asInstanceOf[x.type *: tup.type] diff --git a/commons/containsOnly.scala b/commons/containsOnly.scala index 1afb3c2..ea58c8c 100644 --- a/commons/containsOnly.scala +++ b/commons/containsOnly.scala @@ -15,30 +15,32 @@ object containsOnly extends containsOnlyLowPriority: def refl[Tup <: Tuple, T]: Tup containsOnly T = reusable - inline given [Tup <: Tuple, T] => (Loop[Tup, T] =:= true) => containsOnly[Tup, T] = refl + given [Tup <: Tuple, T](using Loop[Tup, T] =:= true): containsOnly[Tup, T] = refl /** A constant map `[_] =>> C` makes every element `C`. Unifies even for abstract `Es`. */ - inline given [Es <: Tuple, C] => (Tuple.Map[Es, [_] =>> C] containsOnly C) = refl + given [Es <: Tuple, C] => (Tuple.Map[Es, [_] =>> C] containsOnly C) = refl /** A covariant `F` gives `F[e] <: F[Any]` for every element (invariant `F` still needs `refl`). */ - inline given [Es <: Tuple, F[+_]] => (Tuple.Map[Es, F] containsOnly F[Any]) = refl + given [Es <: Tuple, F[+_]] => (Tuple.Map[Es, F] containsOnly F[Any]) = refl - inline given [Tup <: Tuple, T](using inline ev: Tup containsOnly T): (Tuple.Tail[Tup] containsOnly T) = refl - inline given [Tup <: Tuple, T](using inline ev: Tup containsOnly T): (Tuple.Reverse[Tup] containsOnly T) = refl - inline given [Tup1 <: Tuple, Tup2 <: Tuple, T]( - using inline ev1: Tup1 containsOnly T, + given [Tup <: Tuple, T](using ev: Tup containsOnly T): (Tuple.Tail[Tup] containsOnly T) = refl + + given [Tup <: Tuple, T](using ev: Tup containsOnly T): (Tuple.Reverse[Tup] containsOnly T) = refl + + given [Tup1 <: Tuple, Tup2 <: Tuple, T]( + using ev1: Tup1 containsOnly T, ev2: Tup2 containsOnly T, ): (Tuple.Concat[Tup1, Tup2] containsOnly T) = refl - inline given [Tup1 <: Tuple, Tup2 <: Tuple, T1, T2]( - using inline ev1: Tup1 containsOnly T1, + + given [Tup1 <: Tuple, Tup2 <: Tuple, T1, T2]( + using ev1: Tup1 containsOnly T1, ev2: Tup2 containsOnly T2, ): (Tuple.Zip[Tup1, Tup2] containsOnly (T1, T2)) = refl - import scala.language.implicitConversions - given [Tup <: Tuple: Of[T], T] => Conversion[Tuple.Head[Tup], T] = _.asInstanceOf[T] + given [Tup <: Tuple, This >: Tup <: Tuple, T](using ev: Tup containsOnly T): (Tuple.Head[This] <:< T) = <:<.refl.asInstanceOf[(Tuple.Head[This] <:< T)] - given [Tup <: Tuple: Of[T], T] => Conversion[Tuple.Last[Tup], T] = _.asInstanceOf[T] + given [Tup <: Tuple, This >: Tup <: Tuple, T](using ev: Tup containsOnly T): (Tuple.Last[This] <:< T) = <:<.refl.asInstanceOf[(Tuple.Last[This] <:< T)] sealed trait containsOnlyLowPriority: - inline given Tuple containsOnly Any = containsOnly.refl + given Tuple containsOnly Any = containsOnly.refl From 337ef373d4f887a01cbd7dc1c442fe7e4feb2461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Kozak?= Date: Fri, 4 Sep 2026 14:32:18 +0200 Subject: [PATCH 2/3] simplify definitions --- commons/TupleOps.scala | 13 ++++--------- commons/containsOnly.scala | 23 +++++++++-------------- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/commons/TupleOps.scala b/commons/TupleOps.scala index 90052ba..9218850 100644 --- a/commons/TupleOps.scala +++ b/commons/TupleOps.scala @@ -12,14 +12,11 @@ extension (tup: Tuple) inline def hasDuplicates: HasDuplicates[tup.type] = compiletime.constValue[HasDuplicates[tup.type]] - def mapAs[T](using ev: tup.type containsOnly T)[F[_ <: T]](f: [t <: T] => t => F[t]) - : Tuple.Map[tup.type, [X] =>> F[X & T]] = + def mapAs[T](using tup.type containsOnly T)[F[_ <: T]](f: [t <: T] => t => F[t]) + : Tuple.Map[tup.type, [X] =>> F[X & T]] = tup.map[[X] =>> F[X & T]]([t] => (t: t) => f(t.asInstanceOf[t & T])) - def toArrayOf[T](using ev: tup.type containsOnly T)(using ClassTag[T]): Array[T] = - toArrayOfImpl[T] - - private def toArrayOfImpl[T: ClassTag]: Array[T] = tup match + def toArrayOf[T](using tup.type containsOnly T)(using ClassTag[T]): Array[T] = tup match case EmptyTuple => Array.empty[T] case self: Product => val arr = new Array[T](self.productArity) @@ -28,9 +25,7 @@ extension (tup: Tuple) arr(i) = self.productElement(i).asInstanceOf[T] i += 1 arr - def to[T](using ev: tup.type containsOnly T)[C](factory: Factory[T, C]): C = - toImpl(factory) - def toImpl[T, C](factory: Factory[T, C]): C = + def to[T](using tup.type containsOnly T)[C](factory: Factory[T, C]): C = factory.fromSpecific(tup.productIterator.asInstanceOf[Iterator[T]]) type Indices[Tup <: Tuple] <: Tuple = Tup match diff --git a/commons/containsOnly.scala b/commons/containsOnly.scala index ea58c8c..00b7a45 100644 --- a/commons/containsOnly.scala +++ b/commons/containsOnly.scala @@ -15,7 +15,7 @@ object containsOnly extends containsOnlyLowPriority: def refl[Tup <: Tuple, T]: Tup containsOnly T = reusable - given [Tup <: Tuple, T](using Loop[Tup, T] =:= true): containsOnly[Tup, T] = refl + given [Tup <: Tuple, T] => (Loop[Tup, T] =:= true) => (Tup containsOnly T) = refl /** A constant map `[_] =>> C` makes every element `C`. Unifies even for abstract `Es`. */ given [Es <: Tuple, C] => (Tuple.Map[Es, [_] =>> C] containsOnly C) = refl @@ -23,24 +23,19 @@ object containsOnly extends containsOnlyLowPriority: /** A covariant `F` gives `F[e] <: F[Any]` for every element (invariant `F` still needs `refl`). */ given [Es <: Tuple, F[+_]] => (Tuple.Map[Es, F] containsOnly F[Any]) = refl - given [Tup <: Tuple, T](using ev: Tup containsOnly T): (Tuple.Tail[Tup] containsOnly T) = refl + given [Tup <: Tuple: Of[T], T] => (Tuple.Tail[Tup] containsOnly T) = refl - given [Tup <: Tuple, T](using ev: Tup containsOnly T): (Tuple.Reverse[Tup] containsOnly T) = refl + given [Tup <: Tuple: Of[T], T] => (Tuple.Reverse[Tup] containsOnly T) = refl - given [Tup1 <: Tuple, Tup2 <: Tuple, T]( - using ev1: Tup1 containsOnly T, - ev2: Tup2 containsOnly T, - ): (Tuple.Concat[Tup1, Tup2] containsOnly T) = refl + given [Tup1 <: Tuple: Of[T], Tup2 <: Tuple: Of[T], T] => (Tuple.Concat[Tup1, Tup2] containsOnly T) = refl - given [Tup1 <: Tuple, Tup2 <: Tuple, T1, T2]( - using ev1: Tup1 containsOnly T1, - ev2: Tup2 containsOnly T2, - ): (Tuple.Zip[Tup1, Tup2] containsOnly (T1, T2)) = refl + given [Tup1 <: Tuple: Of[T1], Tup2 <: Tuple: Of[T2], T1, T2] => (Tuple.Zip[Tup1, Tup2] containsOnly (T1, T2)) = refl + given [Tup <: Tuple: Of[T], This >: Tup <: Tuple, T] => (Tuple.Head[This] <:< T) = + <:<.refl.asInstanceOf[(Tuple.Head[This] <:< T)] - given [Tup <: Tuple, This >: Tup <: Tuple, T](using ev: Tup containsOnly T): (Tuple.Head[This] <:< T) = <:<.refl.asInstanceOf[(Tuple.Head[This] <:< T)] - - given [Tup <: Tuple, This >: Tup <: Tuple, T](using ev: Tup containsOnly T): (Tuple.Last[This] <:< T) = <:<.refl.asInstanceOf[(Tuple.Last[This] <:< T)] + given [Tup <: Tuple: Of[T], This >: Tup <: Tuple, T] => (Tuple.Last[This] <:< T) = + <:<.refl.asInstanceOf[(Tuple.Last[This] <:< T)] sealed trait containsOnlyLowPriority: given Tuple containsOnly Any = containsOnly.refl From 3de0c2070d72496b9abc50dd7b127fd577f917cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Kozak?= Date: Fri, 4 Sep 2026 14:35:07 +0200 Subject: [PATCH 3/3] remove import scala.language.implicitConversions --- test/ContainsOnlyTest.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/ContainsOnlyTest.scala b/test/ContainsOnlyTest.scala index 6474139..770985d 100644 --- a/test/ContainsOnlyTest.scala +++ b/test/ContainsOnlyTest.scala @@ -2,8 +2,6 @@ package halotukozak.commons import halotukozak.commons.containsOnly.given -import scala.language.implicitConversions - class ContainsOnlyTest extends munit.FunSuite: // --- Loop type-level: positive cases ---