Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

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)")
  }
}
share|improve this question
    
your code won't even compile as given... –  Kim Jan 5 '13 at 16:26
    
@Kim I have provided a more complete sample for the the second version. Maybe the imports will make a difference for you? It compliles fine for me on 2.10.0-RC5. Thanks! –  vossad01 Jan 5 '13 at 16:54
1  
getAction[Double]("sqrt")(Seq(4.0)) still doesn't compile. However, getAction[Double]("sqrt").apply(Seq(4.0)) does. –  Kim Jan 5 '13 at 17:03
    
@Kim Huh, that its unexpected, inlining the result of getAction (see the TestSuite I added) is not safe. I guess now I need to research why that does not work. I my guess is that it wants to use Seq(4.0) as the implicit parameter. Thanks for pointing that out! I am just learning scala. –  vossad01 Jan 5 '13 at 17:18
    
Yes, that is indeed the reason. –  Kim Jan 5 '13 at 20:55
add comment

1 Answer

up vote 1 down vote accepted

How about not having to specify the type at all?

implicit class SeqWithAction[T:Fractional](seq:Seq[T]) {
  def getAction( op : String ) : String =
    op match {
      case "add" => seq.sum.toString
      case "product" => seq.product.toString
      case "mean" => {
        implicit def intToFractional(i : Int) =
          implicitly[Fractional[T]].fromInt(i);
        (seq.sum / seq.length).toString
      }
      case "sqrt" => seq.map(i => math.sqrt(i.toDouble)).toString
    }
  }     
}
Seq(4.0).getAction("sqrt")
share|improve this answer
    
I like the result with respect to reductin code. Would this be considered good form in Scala? I am coming from mainly a C# background, and there this would likely be considered an abuse of extention methods. –  vossad01 Jan 5 '13 at 17:20
    
I think this is quite common in Scala. It's okay as long as you put your implicits into a separate object so the user of your library can choose whether to import them or not. –  Kim Jan 5 '13 at 20:53
    
You find this kind of thing a lot in DSLs, e.g. for testing. –  Kim Jan 5 '13 at 20:54
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.