Reputation: 5020
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:
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.
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.
Upvotes: 1
Views: 87
Reputation: 430
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.
Upvotes: 0