1

How can you make code in a Scala library call type-specific code for objects supplied by a caller to that library, where the decision about which type-specific code to call is made at compile-time (statically), not at run-time?

To illustrate the concept, suppose I want to make a library function that prints objects one way if there's a CanMakeDetailedString defined for them, or just as .toString if not. See nicePrint in this example code:

  import scala.language.implicitConversions

  trait CanMakeDetailedString[A] extends (A => String)

  def noDetailedString[A] = new CanMakeDetailedString[A] {
    def apply(a: A) = a.toString
  }

  object Util {
    def nicePrint[A](a: A)
      (implicit toDetail: CanMakeDetailedString[A] = noDetailedString[A])
    : Unit = println(toDetail(a))

    def doStuff[A](a: A)
    : Unit = { /* stuff goes here */ nicePrint(a) }
 }

Now here is some test code:

  object Main {
    import Util._

    case class Rototiller(name: String)

    implicit val rototillerDetail = new CanMakeDetailedString[Rototiller] {
      def apply(r: Rototiller) = s"The rototiller named ${r.name}."
    }

    val r = Rototiller("R51")

    nicePrint(r)
    doStuff(r)
  }

Here's the output in Scala 2.11.2:

The rototiller named R51.
Rototiller(R51)

When I call nicePrint from the same scope where rototillerDetail is defined, the Scala compiler finds rototillerDetail and passes it implicitly to nicePrint. But when, from the same scope, I call a function in a different scope (doStuff) that calls nicePrint, the Scala compiler doesn't find rototillerDetail.

No doubt there are good reasons for that. I'm wondering, though, how can I tell the Scala compiler "If an object of the needed type exists, use it!"?

I can think of two workarounds, neither of which is satisfactory:

  1. Supply an implicit toDetail argument to doStuff. This works, but it requires me to add an implicit toDetail argument to every function that might, somewhere lower in the call stack, have a use for a CanMakeDetailedString object. That is going to massively clutter my code.

  2. Scrap the implicit approach altogether and do this in object-oriented style, making Rototiller inherit from CanMakeDetailedString by overriding a special new method like .toDetail.

Is there some technique, trick, or command-line switch that could enable the Scala compiler to statically resolve the right implicit object? (Rather than figuring it out dynamically, when the program is running, as in the object-oriented approach.) If not, this seems like a serious limitation on how much use library code can make of "typeclasses" or implicit arguments. In other words, what's a good way to do what I've done badly above?


Clarification: I'm not asking how this can be done with implicit val. I'm asking how you can get the Scala compiler to statically choose type-appropriate functions in library code, without explicitly listing, in every library function, an implicit argument for every function that might get called lower in the stack. It doesn't matter to me if it's done with implicits or anything else. I just want to know how to write generic code that chooses type-specific functions appropriately at compile-time.

Ben Kovitz
  • 4,920
  • 1
  • 22
  • 50
  • 1
    There's a related discussion here: http://stackoverflow.com/questions/3255991/scala-reconciling-type-classes-with-dependency-injection?rq=1 – NietzscheanAI Mar 23 '16 at 21:06
  • so is this answered then? – slouc Sep 09 '16 at 09:06
  • @slouc Not that I'm aware of. Perhaps the question has been [anæsthetized](http://stackoverflow.com/questions/3255991/scala-reconciling-type-classes-with-dependency-injection?rq=1#comment3366841_3256419). – Ben Kovitz Sep 09 '16 at 18:45

1 Answers1

0

implicits are resolved at compile time so it can't know what A is in doStuff without more information. That information can be provided through an extra implicit parameter or a base type / interface as you suggested.

You could also use reflection on the A type, use the getType that returns the child type, cast the object to that type, and call a predefined function that has the name of the type that writes the string details for you. I don't really recommend it as any OOP or FP solution is better IMHO.

Jonathan Nappee
  • 430
  • 2
  • 11
  • Does this mean that what I'm asking for can't be done (choose the right function statically, etc.)? If I understand you correctly (hopefully I'm wrong), typeclasses are basically crippled in Scala unless you "explicitly" make each function take implicit arguments for all typeclasses that could be needed lower in the stack. – Ben Kovitz Mar 23 '16 at 20:37
  • That is my understanding. Another solution would be to have a switch on A's type in nicePrint. It seems Haskell is better at this. – Jonathan Nappee Mar 24 '16 at 11:43