Implicits for the Masses
I just finished reading Tony Morris’s blog post on Scala implicits and saw the following comment:
To me, implicits look a lot like global variables, which is why I don’t like them.
Or maybe I missed something?
Uh, yep — you missed something
I was going to reply in the comments to Tony’s post but as per usual it became far too long, so I’m posting it here instead.
Let’s try something a little less abstract than what was covered in Tony’s article. I haven’t actually tested any of this code to check that it compiles (lazy, lazy, lazy!), but it should be pretty straightforward & clarify what implicits can be used for:
class Person(name : String) {}
class Conversation(person1 : Person, person2 : Person) {
def greet() = {
println(person1.name + " says: Hello, " + person2.name)
println(person2.name + " says: Oh, hello " + person1.name)
}
}
implicit def stringToPerson(name : String) : Person = new Person(name)
With this implicit in effect, we can create a Conversation without even mentioning the Person class:
val conv = new Conversation("John", "Mary")
conv.greet()
See how we passed two strings to the Conversation constructor, even though it’s supposed to take two Persons?
Global variables don’t really come into it, it’s more about implicit type conversion.
Why would you do this? Well for one, it lets you create faux multimethods:
class FrameWrapper(frame : JFrame) {
def title_=(s : String) = frame.setTitle(s)
def visible_=(b : Boolean) = frame.setVisible(b)
}
implicit def wrapFrame(frame : JFrame) : FrameWrapper = new FrameWrapper(frame)
This means we can call FrameWrapper methods on a JFrame and the Scala compiler will figure out what we mean:
val frame = new JFrame
frame.title = "My Application"
frame.visible = true
Normally you’d have to call setTitle/setVisible at the second and third lines of the code above because JFrame doesn’t have any public attributes called “title” and “visible”. However, with the implicits in effect the Scala compiler will wrap your JFrame in a FrameWrapper instance to call that method. A weak example to be sure, but it really is a nice way to “scala-ize” any existing Java code. All this is statically checked too, so it won’t try to set your children on fire when you’re not looking.
Nifty eh?
Thanks Thomas, as you can see I wasn’t doing too well to point this out
This is actually the kind of implicit that I understand and appreciate (type conversion), but I don’t think you have addressed my point, which is the other type of implicit that lets you pass “invisible” parameters.
In the example I pasted on Tony’s blog, I can call a method “speakImplicitly” without any parameter and the method magically receives “hello world”.
To me, this looks a lot like the following snippet:
static String hello = “Hello world”;
public void speakImplicitly() {
System.out.println(hello);
}
which is universally acknowledged as being bad code.
How are implicits different?
A legitimate concern, I guess. But that’s not really the case here.
Obviously you wouldn’t use implicit parameters if it doesn’t make sense. Bad code is still bad code, and you can use implicits for evil. That doesn’t mean you have to. As you’ve shown yourself, the same evilness is just as easy to achieve using Java.
Implicit parameters are much more powerful than global variables, however. Scala’s documentation provides a good example of where implicit parameters might make sense.
In any case, the implicit values need only to be present in the calling scope of the method with the implicit parameter — which is what I think Tony was trying to convey when he said that the implicit values aren’t global. Here’s an example:
Hope that helps, Sean. If not, don’t hesitate to interrogate me some more.
UPDATE: I’m typing like a chimp — corrected some typos.
To clarify, the implicit values defined in Speaker1 are local to that class. They will not be available outside of that class. Likewise, the implicit value defined in speakWithLocal is local to that method.
Interesting post.
I just love the irony of explicitly defining implicits
Tom,
The scope of the implicit doesn’t matter much, the important part is that it’s outside the method that’s using it, and that’s what is really bothering me.
I would think this should be even more of a concern for functional programmers like yourself and Tony, but you seem to be giving a puzzling free pass to Scala on that respect.
I just don’t feel comfortable using a method whose behavior will change silently depending on what mixin I use.
Sean: I understand the uneasiness. I’d bet if such a feature were used without due diligence, you’d wind up with a big mess on your hands.
Having said that, rather than just calling the feature awful for some arbitrary example of how it might be used for evil, I’d be open to using implicit parameters if it made my code more readable. I certainly wouldn’t put them in the same boat as globals, not even from a pure perspective — that a value is implicitly passed to a function doesn’t suddenly make it mutable nor does its use imply side effects.
So polymorphism is right out the window then? Boy, you’re a hard one to please
Anyway, the fact a feature exists doesn’t necessitate its use. Use language features where they make sense.
In all seriousness, I’m sure you could use implicit values in a way that’s completely confusing and baffling. My advice is to try not to.
Oh, and I’m not a functional die-hard — just a general language geek. Honest.
I realize I’m coming a bit late to this conversation. (curse you, timezone offsets!) Let’s see if I can catch up…
As already pointed out, the primary use of implicit *anything* in Scala is for type coercion. The reason for this as a separate language construct is to avoid the random and almost uncontrolled coercion that we have in Java and C (C has a bit more control, but it’s still difficult to deal with).
So anyway, the idea is that you mark a function as `implicit` and it will be automatically inserted as needed (I realize this has already been addressed, I’m just trying to lay the groundwork for my next point):
implicit def str2int(str: String) = str.toInt
val one: Int = “1″
The second line becomes:
val one: Int = str2int(”1″)
The unfortunate truth is that the addition of this language construct *demands* the introduction of the implicit parameters, otherwise it is no where near as useful. Remember that implicit functions are lexically scoped (just like everything else in Scala). This means that a type conversion can exist in one scope, but on in the one you really need it. We can imagine this as a problem in the following sort of scenario:
def doSomething(value: Option[Int]) = value match {
case Some(i) => i
case None => 0
}
All this does is unbind the Intfrom the Option. Now, we have already defined an implicit conversion from String to Int, but even with this conversion, we cannot do the following:
val param = Some(”123″)
doSomething(param) // ERROR!
The reason is that the conversion must be applied *inside* the doSomething, and the conversion is simply not in scope within that method (which is presumably in some other class). In fact, allowing this method to see the conversion as being in scope would break encapsulation horribly and really be some cause for concern. The solution is to use an implicit parameter:
def doSomething[T](value: Option[T])(implicit def conv2int(param: T): Int): Int =
value match {
case Some(i) => i
case None = 0
}
Now we can pass to this method an Option of *any* type which has some conversion to Int. This is far more flexible than our previous version, and just as type-safe. We have avoided breaking the scoping laws, and we still managed to make the implicit conversion available to the doSomething method. Oh, and btw, by currying the function in that way (the double-parens notation), we have ensured that the method signature has not been changed by requiring this additional parameter. As a matter of interest, we could express this using an equivalent (but more concise) syntax (note, I haven’t checked this and I don’t often use this syntax, so I might have gotten it slightly wrong):
def doSomething[Int <% T](value: Option[T]): Int
The issue you are raising has to do with implicit parameters as variables (using that term loosely, since idiomatic Scala “variables” are actually constants). e.g. the following:
implicit val name = “Daniel”
def doSomething(implicit val name: String) = name ” Spiewak”
doSomething // implicitly grabs parameter
I agree this is ugly and nasty. It’s nothing like global variables (since everything is still scoped), but it *is* bad design on the developer’s part. The truth is that this is a misuse of the language feature. It is *allowed* because variables are actually methods in disguise, and to keep the language consistent, the implicit modifier must be applicable to any method, even if it’s a variable. There are still times when this is desirable (as was pointed out, Scala’s documentation has a nice example), but in the general case, I think this should be avoided.
The good news: it is avoided! I don’t know of anyone who structures their code like this. Yes, there are implicit params from time to time, but always to satisfy my first example (passing the implicit conversion implicitly) and not to save a little typing with the variables.
Does this make a bit more sense?
/me looks on in disbelief
Wow, well that was informative. Thanks, Daniel!
Sean -
Implicits in Scala are lexically scoped, unlike globally open classes in Ruby. I blogged about it sometime back. http://debasishg.blogspot.com/2008/02/why-i-like-scalas-lexically-scoped-open.html .. If this is of any help to you ..
Cheers.
- Debasish
Excellent explanation, Daniel. I’m one of those guys following the Scala world, but not having done anything with it yet. Several articles on implicits left me a bit puzzled, but this comment was very insightful. Consider turning it into a blog article.
– Thomas
I like implicits. For me, what feels uneasy about it is that, in a snippet like this…
val frame = new JFrame
frame.title = “My Application”
frame.visible = true
…a wrapper is being created and discarded twice at runtime. Or at least so I assume - am I right? is this considered a problem?
German,
Theoretically speaking, that’s not necessarily the case. Remember that the language is statically typed, so a fair bit can actually be inferred by the compiler. The fact a wrapper object will be required for two separate calls can be determined at compile time, leaving the compiler to determine whether it should generate code to make calls to “frame” or to its implicit counterpart.
Whether or not this is true in practice is another question entirely, but it really wouldn’t surprise me if that were the case for this trivial example.
Sean, I agree that the Java code you posted is “universally acknowledged as being bad code”. But if you change hello to be declared final, then I think it’s no longer bad code at all! Strings are immutable, the variable is final…. no problem.
I think it’s fine for the behavior of code to depend on a value which is defined in an enclosing scope, in fact, it’s good. It makes that code configurable, which you sometimes want and need. You only run into trouble if the value is mutable, or if the variable it’s stored in is assignable (that is, non-final).
Seth got it right. Notice that an objector often resorts to introducing side-effects (inadvertently?) in order to undermine high-level abstractions. I glossed over this common occurrence in the referenced article. It’s a tough one to address and is often very confrontational (and even traumatic for some!), especially over an internet forum.
Some people’s brains just won’t bend that way; they have far too many defences against it. Cognitive dissonance (doublethink) is the barrier.