I want to create a Vec
type alias for tuple where Vec[Int, 3]
is the same as writing (Int, Int, Int)
. This is basically a fixed-length collection, but with also really nice syntax for creating them (since they really are just tuples). But then to make this nice it would be good to also have more methods for this type, like a nicer map, because the polymorphic one for tuples is cumbersome to use, but using this type alias it is guaranteed that all elements will be of the same type, so we can just create a normal map-function that returns a Vec of the same length (or arity i suppose).
I currently have the following type:
import scala.compiletime.ops.int.S
type Vec[+T, Size <: Int] <: Tuple = Size match
case 0 => EmptyTuple
case S[n] => T *: Vec[T, n]
extension [A, Size <: Int](v: Vec[A, Size])
def map(f: A => B): Vec[B, Size] = ???
Is this a good way to go about this? Is there some other way which makes this nicer? How do I even create the map function with this type alias?
1 Like
There is (not extension) method Tuple#map
already, so it would conflict with your extension Vec#map
.
Try
inline def map[A, B, Size <: Int](v: Vec[A, Size], f: A => B): Vec[B, Size] =
inline erasedValue[Size] match
case _: 0 => EmptyTuple
case _: S[n] => v match
case (a *: as): (A *: Vec[A, `n`]) => f(a) *: map[A, B, n](as, f)
2 Likes
Thank you, this is really cool! I tried using it, but found that i have to specify the type parameters myself, it would be nice if the compiler could derive the parameters itself. Is that possible? Like can the size, for example, be derived to be the size of the tuple? That would make this code work too which would be very nice!
import scala.compiletime.ops.int.S
import scala.compiletime.erasedValue
type Vec[+T, Size <: Int] <: Tuple = Size match
case 0 => EmptyTuple
case S[n] => T *: Vec[T, n]
extension [A, Size <: Int](v: Vec[A, Size])
inline def vmap[B](f: A => B): Vec[B, Size] =
inline erasedValue[Size] match
case _: 0 => EmptyTuple
case _: S[n] => (v: @unchecked) match
case ((a: A) *: (as: Vec[A, `n`])) => f(a) *: as.vmap[B](f)
@main def main =
val v: Vec[Int, 3] = (1, 2, 3)
val w: Vec[Int, 3] = v.vmap(_ + 1)
println(w)