I'm learning Scala multi-stage metaprogramming. I'm currently working on an exercise that asks me to implement the dot-product (sum-of-product) between two vectors of the same length.
Writing the code, I need to match at compile-time Arrays and check if their length equals, before proceeding with the dot product. Otherwise, an exception is thrown. I'm using a FromExpr implicit object to specify the extraction by implementing its unapply method. And, this is what I came up with
given ArrayFromExpr: FromExpr[Array[Int]] with
def unapply(x: Expr[Array[Int]])(using Quotes): Option[Array[Int]] =
x match
case '{ Array[Int]()(using $ct1) } => Some(Array[Int]())
case '{ Array[Int]((${Varargs(Exprs(elements))})*) } => Some(Array(elements*))
case _ => None
The problem is, I get a None as a result in the enclosing match-case statement. Which is,
def dotImpl(v1: Expr[Array[Int]], v2: Expr[Array[Int]])(using q: Quotes): Expr[Int] = {
(v1.value, v2.value) match {
case (Some(arr1), Some(arr2)) if arr1.length != arr2.length => throw RuntimeException("Cannot compute dot product of arrays having different lengths.")
case (Some(arr1), Some(arr2)) if arr1.length == 0 => '{ 0 }
case (Some(arr1), Some(arr2)) => computeDotProduct(arr1, arr2)
case (None, None) => '{ 99 }
case _ => '{ 999 }
}
}
This last snippet is not completed yet, but I'm trying to get past this wrong behaviour before continuing. I do get a correct behaviour for the case of empty Array (defined as val arr = Array[Int]()).
The test that allows me to asses the misbehaviour of the FromExpr implementation is
"dot product for different length arrays" should "throw" in {
val v1 = Array[Int](2, 1)
val v2 = Array[Int](1, 2, 3)
dot(v1, v2) shouldBe 1 // this is for a better test output
a [RuntimeException] should be thrownBy {
dot(v1, v2)
}
}
which results in
99 was not equal to 1
While a RuntimeException is expected.
Any help or advice?