Vec type alias for Tuple

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)