I am trying to create a Fractional[Int]
instance in Scala 3 that I want to use for finite field arithmetic.
I have a class whose instances can work as Fractional[Int]
implementations:
class IntModp(val p : Int) extends Fractional[Int] {
def inverse(a : Int) : Int = {
// implementation of modular inverse with fast Euclidean algorithm here
}
val inverses : Map[Int, Int] = Map.from(
Range(1, p-1).map(j => (j -> inverse(j))) ++
Range(-(p-1), -1).map(j => (j -> -inverse(-j)))
)
def norm(a: Int): Int = {
val r : Int = a % p
return r match {
case rr : Int if (rr < -(p-1)/2) => rr + p
case rr : Int if (rr > (p-1)/2) => rr-p
case rr : Int => rr
}
}
def compare(x: Int, y: Int): Int = Ordering.Int.compare(norm(x), norm(y))
def div(x: Int, y: Int): Int = times(x, inverses(norm(y)))
def fromInt(x: Int): Int = norm(x)
def minus(x: Int, y: Int): Int = norm(x - y)
def negate(x: Int): Int = norm(-x)
def parseString(str: String): Option[Int] =
IntIsIntegral.parseString(str).map(j => norm(j))
def plus(x: Int, y: Int): Int = norm(x + y)
def times(x: Int, y: Int): Int = norm(x * y)
def toDouble(x: Int): Double = IntIsIntegral.toDouble(norm(x))
def toFloat(x: Int): Float = IntIsIntegral.toFloat(norm(x))
def toInt(x: Int): Int = IntIsIntegral.toInt(norm(x))
def toLong(x: Int): Long = IntIsIntegral.toLong(norm(x))
}
According to my understanding of Scala 3 type classes, and of scala.math.Fractional.Implicits
, I should now be able to do something like the following:
given IntMod17 : Fractional[Int] = new IntModp(17)
15 + 12 // expect to see -7, instead see 27
15 / 12 // expect to see -3, instead see 1
But instead, whenever I actually use any of the arithmetic operators I get the "normal" Int
behavior. I can of course do something like
given IntMod17 : Fractional[Int] = new IntModp(17)
val fr = summon[Fractional[Int]] // fr is now this IntMod17 object
fr.plus(15, 12) // expect to see -7, and see -7
fr.div(15, 12) // expect to see -3, and see -3
The class Int
already has methods (normal, not extension methods) +
, /
. You can't change their behavior.
In Scala if for x.foo
there are an extension method foo
(defined with implicits or in Scala 3 with extension
keyword) and a normal method foo
then the latter method wins.
An example:
class A:
def foo = println("normal method")
implicit class AOps(a: A):
def foo = println("extension method 1")
extension (a: A)
def foo = println("extension method 2")
class AOps1(a: A):
def foo = println("extension method 3")
given Conversion[A, AOps1] = AOps1(_)
trait MyTypeclass[X]:
def foo = println("extension method 4")
extension [X: MyTypeclass](a: X)
def foo = summon[MyTypeclass[X]].foo
given MyTypeclass[A] with {}
trait MyTypeclass1[X]:
extension (a: X)
def foo = println("extension method 5")
given MyTypeclass1[A] with {}
val a = new A
a.foo // normal method