4

let's say that I would like to add some method to String class. But concrete implicit class that should be applied is known during runtime (strategy pattern). Lets say we have

trait StringExtensions {
  def doSth(str: String): String
}

class Strategy1 extends StringExtensions {
   override def doSth(str: String): String = "a"
}

class Strategy2 extends StringExtensions {
   override def doSth(str: String): String = "b"
}

now my client code looks like:

def someMethod(strategy: StringExtensions) : String{
  val name = "Pawel"
  return strategy.doSth(name)
}
...
String ret = someMethod(new Strategy1())

but I would like to have code like:

def someMethod(strategy: StringExtensions) : String{
  val name = "Pawel"
  return name.doSth() // Here is the tricky line
}
...
String ret = someMethod(new Strategy1())

I played a bit with implicits but when comes to this use case with inheritance I cannot find proper solution, any help?

1 Answer 1

5

I'm not sure you should really use implicits like this, but maybe in some DSL this might be a valid use case.

class StringExtensions(str: String, strategy: StringExtensionsStrategy) {
  def doSth() = strategy.doSth(str)
}

trait StringExtensionsStrategy extends (String => StringExtensions) {
  final def apply(str: String) = new StringExtensions(str, this)
  def doSth(str: String): String
}

class Strategy1 extends StringExtensionsStrategy {
   override def doSth(str: String) = "a"
}

class Strategy2 extends StringExtensionsStrategy {
   override def doSth(str: String) = "b"
}

def someMethod(implicit strategy: StringExtensionsStrategy) = {
  val name = "Pawel"
  name.doSth()
}

val ret: String = someMethod(new Strategy1())

As mentioned in the comments, an alternative encoding would be this:

class StringExtensions(str: String, strategy: StringExtensionsStrategy) {
  def doSth() = strategy.doSth(str)
}

trait StringExtensionsStrategy {
  implicit final def apply(str: String) = new StringExtensions(str, this)
  def doSth(str: String): String
}

class Strategy1 extends StringExtensionsStrategy {
   override def doSth(str: String) = "a"
}

class Strategy2 extends StringExtensionsStrategy {
   override def doSth(str: String) = "b"
}

def someMethod(strategy: StringExtensionsStrategy) = {
  import strategy._
  val name = "Pawel"
  name.doSth()
}

val ret: String = someMethod(new Strategy1())
Sign up to request clarification or add additional context in comments.

5 Comments

Implicit strategy parameter could be replaced with implicit val s = strategy as a first line in someMethod.
Or import strategy._ if you make apply method in StringExtensionsStrategy trait implicit.
You could do that, but after having to add some extra implicit vals and imports, you could just as easily have written strategy.doSth(name). That's why I say that this is probably not a good idea, but there might be some use case, and I assumed that you will want to eliminate as much boilerplate as you can in that hypothetical use case.
Thank you guys, of course my example is pretty trivial, I will use it to construct some dsl. Could give me some hints where should I look for the explanation of this code?
@PawełKaczorowski It just uses functions and implicits. You can look here for an overview.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.