Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

scala: override implicit parameter to constructor

Tags:

scala

I have a class that takes an implicit parameter which is used by functions called inside class methods. I want to be able to either override that implicit parameter, or alternatively, have the implicit argument be copied from its source. As an example:

def someMethod()(implicit p: List[Int]) {
  // uses p
}

class A()(implicit x: List[Int]) {

  implicit val other = List(3) // doesn't compile

  def go() { // don't want to put implicit inside here since subclasses that override go() have to duplicate that
    someMethod()
  }
}

The behavior I want is that someMethod() gets an implicit parameter that is some changed version of x, which was the class's implicit parameter. I want to be able to either mutate x without changing it for whatever passed it into A's constructor, or otherwise override it to a new value of my choosing. Both approaches don't seem to work. That is, it doesn't copy the list in the former case, and the compiler finds an ambiguous implicit value for the latter case. Is there a way to do this?

I realize that I can redefine the implicit value within go(), but this is not a good choice in my case because this class is subclassed numerous times, and I'd like to handle this implicit change in the base class only. So it doesn't necessarily need to go in the constructor, but it must be in a method other than go().

like image 427
Heinrich Schmetterling Avatar asked May 11 '11 09:05

Heinrich Schmetterling


2 Answers

Introduce another wrapper type, simply to disambiguate:

//  badly named, choose something domain-specific
case class ListHolder(theList: List[Int])

def someMethod()(implicit holder: ListHolder) {
  val xs = holder.theList
  // uses xs ...
}

class A()(implicit xs: List[Int]) {

  implicit val other = ListHolder(42 :: xs) // compiles

  def go() {
    // xs is never considered for the implicit param to someMethod()
    // because it's now the wrong type
  }
}

This also makes the code more self-documenting, as it becomes blindingly obvious that the two implicits are not one and the same.

like image 134
Kevin Wright Avatar answered Sep 30 '22 04:09

Kevin Wright


If you want to have zillions of implicits floating around that don't collide with each other, you can create a wrapper class that you can tag with marker traits for implicit usage. There are a variety of syntaxes you could use; here's one example:

object Example {
  class Implication[A,B](val value: A) {
    def apply[C](c: C) = new Implication[C,B](c)
  }
  object Implication {
    def mark[B] = new Implication[Unit,B](())
    implicit def implication_to_value[A,B](i: Implication[A,B]) = i.value
  }

  trait One {}
  trait Two {}
  implicit val x = Implication.mark[One]("Hello")
  implicit val y = Implication.mark[Two]("Hi")

  def testOne(implicit s: Implication[String,One]) = println(s: String)
  def testTwo(implicit s: Implication[String,Two]) = println(s: String)
  def testThree(s: String) = println("String is " + s)

  def main(args: Array[String]) {
    testOne
    testTwo
    testThree(x)
    testThree(y)
  }
}

Which works as you would hope:

scala> Example.main(Array())
Hello
Hi
String is Hello
String is Hi

Since you have to use a wrapper object, it's not super-efficient, but it can be very effective. (Or very confusing, given how much happens implicitly.)

like image 24
Rex Kerr Avatar answered Sep 30 '22 04:09

Rex Kerr