0

I have a scenario where I would like to call sum on a sequence of (Double, Double) tuples. Ideally I would like to do something like the following:

implicit def toTupleNumeric[T](num: Numeric[T]) = new Numeric[(T, T)] { def plus(x: (T, T), y: (T, T)): (T, T) = (num.plus(x._1, y._1), num.plus(x._2, y._2)) def minus(x: (T, T), y: (T, T)): (T, T) = (num.minus(x._1, y._1), num.minus(x._2, y._2)) def times(x: (T, T), y: (T, T)): (T, T) = (num.times(x._1, y._1), num.times(x._2, y._2)) def negate(x: (T, T)): (T, T) = (num.negate(x._1), num.negate(x._2)) def fromInt(x: Int): (T, T) = (num.fromInt(x), num.fromInt(x)) def toInt(x: (T, T)): Int = num.toInt(x._1) + num.toInt(x._2) def toLong(x: (T, T)): Long = num.toLong(x._1) + num.toLong(x._2) def toFloat(x: (T, T)): Float = num.toFloat(x._1) + num.toFloat(x._2) def toDouble(x: (T, T)): Double = num.toDouble(x._1) + num.toDouble(x._2) def compare(x: (T, T), y: (T, T)): Int = num.compare(x._1, y._1) match { case c if c == 0 => num.compare(x._2, y._2) case c => c } } 

But when I call sum:

val seq: Seq[(Double, Double)] = ... val sum = seq.sum 

I get a compiler error:

could not find implicit value for parameter num: Numeric[(Double, Double)]

Is there a way to implement such an implicit?

1 Answer 1

3

You seem to confuse conditional implicit

implicit def toTupleNumeric[T](implicit num: Numeric[T]): Numeric[(T, T)] = ... 

with implicit conversion

implicit def toTupleNumeric[T](num: Numeric[T]): Numeric[(T, T)] = ... 

With the former you're specifying that the data type (T, T) (aka scala.Tuple2[T, T]) is an instance of the type class Numeric provided that T is an instance of the type class. This means that if there is an implicit of the type Numeric[T] then there is an implicit of the type Numeric[(T, T)]. In Scala 3 the type of this conditional implicit is Numeric[T] ?=> Numeric[(T, T)] (aka ContextFunction1[Numeric[T], Numeric[(T, T)]]).

With the latter you're specifying that the data type Numeric[T] can be used where the data type Numeric[(T, T)] is expected and this function Numeric[T] => Numeric[(T, T)] (aka Function1[Numeric[T], Numeric[(T, T)]]) should be used for such transformation automatically.

I guess you meant the former. So add implicit to the parameter (num: Numeric[T]) making it an implicit parameter (implicit num: Numeric[T]). Your code will compile then.

You can refresh your understanding implicits in Scala:

Understanding implicit in Scala

What are type classes in Scala useful for?

Implicit conversion vs. type class

How can I chain implicits in Scala?

Why are implicit conversion deprecated in scala?

Can someone explain me implicit conversions in Scala?

Scala - Implicit conversion to implicit argument

Implicit conversion with implicit parameter

https://docs.scala-lang.org/tour/implicit-parameters.html https://docs.scala-lang.org/tour/implicit-conversions.html

https://docs.scala-lang.org/scala3/book/ca-contextual-abstractions-intro.html ...

https://docs.scala-lang.org/scala3/reference/contextual/index.html ...

You can also rewrite your definition using a context bound (: Numeric), importing implicits, extension methods aka type-class syntax (.toInt, .+(...)), and type-class materializer (Numeric.apply[T])

import Numeric.Implicits._ implicit def toTupleNumeric[T: Numeric]: Numeric[(T, T)] = new Numeric[(T, T)] { override def plus(x: (T, T), y: (T, T)): (T, T) = (x._1 + y._1, x._2 + y._2) override def minus(x: (T, T), y: (T, T)): (T, T) = (x._1 - y._1, x._2 - y._2) override def times(x: (T, T), y: (T, T)): (T, T) = (x._1 * y._1, x._2 * y._2) override def negate(x: (T, T)): (T, T) = (-x._1, -x._2) override def fromInt(x: Int): (T, T) = (Numeric[T].fromInt(x), Numeric[T].fromInt(x)) override def toInt(x: (T, T)): Int = x._1.toInt + x._2.toInt override def toLong(x: (T, T)): Long = x._1.toLong + x._2.toLong override def toFloat(x: (T, T)): Float = x._1.toFloat + x._2.toFloat override def toDouble(x: (T, T)): Double = x._1.toDouble + x._2.toDouble override def compare(x: (T, T), y: (T, T)): Int = Numeric[T].compare(x._1, y._1) match { case c if c == 0 => Numeric[T].compare(x._2, y._2) case c => c } override def parseString(str: String): Option[(T, T)] = ??? } 

What is a "context bound" in Scala?

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.