I have the following function that looks up a mathematical operation that can be applied to a Numeric sequence based on a String
def getAction[T : Fractional]( op : String ) : Seq[T] => String =
op match {
case "add" => seq => seq.sum.toString
case "product" => seq => seq.product.toString
case "mean" => seq => {
implicit def intToFractional(i : Int) =
implicitly[Fractional[T]].fromInt(i);
(seq.sum / seq.length).toString
}
case "sqrt" => seq => seq.map(i => math.sqrt(i.toDouble)).toString
}
That works fine, except I would prefer to specify the type when applying the returned function rather than at the time or retrieval.
i.e. I want
getAction("sqrt")[Double](Seq(4.0))
rather than
getAction[Double]("sqrt")(Seq(4.0))
The only way I have found to do this is by defining a new class/trait with a generic context-bound apply.
abstract class Action {
def apply[T : Fractional](seq : Seq[T]) : String
}
Edit: Replaced with whole class rather than just the method because compilable solution was wanted.
object Main extends App {
import scala.math.Fractional
import Fractional.Implicits._
import scala.language.implicitConversions
abstract class Action {
def apply[T : Fractional](seq : Seq[T]) : String
}
def getAction( op : String ) : Action =
op match {
case "add" =>
new Action {
def apply[T : Fractional](seq : Seq[T]) = seq.sum.toString
}
case "product" =>
new Action {
def apply[T : Fractional](seq : Seq[T]) = seq.product.toString
}
case "mean" =>
new Action {
def apply[T : Fractional](seq : Seq[T])= {
implicit def intToFractional(i : Int) =
implicitly[Fractional[T]].fromInt(i);
(seq.sum / seq.length).toString
}
}
case "sqrt" =>
new Action {
def apply[T : Fractional](seq : Seq[T]) =
seq.map(i => math.sqrt(i.toDouble)).toString
}
}
def getAction_v1[T : Fractional]( op : String ) : Seq[T] => String =
op match {
case "add" => seq => seq.sum.toString
case "product" => seq => seq.product.toString
case "mean" => seq => {
implicit def intToFractional(i : Int) =
implicitly[Fractional[T]].fromInt(i);
(seq.sum / seq.length).toString
}
case "sqrt" => seq => seq.map(i => math.sqrt(i.toDouble)).toString
}
}
However, this seems messy to be because of all the repeated anonymous class definitions and repeated type signatures. This makes the actual math operations less obvious than the first version.
Is there a better way?
Edit: here is a simple test suite that shows execution of both versions import org.scalatest.FunSuite
import org.scalatest.FunSuite
class Tests extends FunSuite {
test("v1"){
val act = Main.getAction_v1[Double]("sqrt")
assert(act(Seq(4.0)) === "List(2.0)")
}
test("v2") {
val act = Main.getAction("sqrt")
assert(act[Double](Seq(4.0)) === "List(2.0)")
}
}
getAction[Double]("sqrt")(Seq(4.0))still doesn't compile. However,getAction[Double]("sqrt").apply(Seq(4.0))does. – Kim Jan 5 at 17:03Seq(4.0)as the implicit parameter. Thanks for pointing that out! I am just learning scala. – vossad01 Jan 5 at 17:18