Is it a bug? Implicit map interferes with nonEmpty call

108 views
Skip to first unread message

Gallus Anonimus

unread,
Oct 22, 2016, 8:53:53 AM10/22/16
to scala-language
Hi,
In following code:

object TheApp {
 
 
def main(args : Array[String]) {
   
implicit val innocentMap = Map("foo" -> Set("bar"))
   
"baz".nonEmpty
 
}
 
}
instead of computing expected true as "baz" string is non empty
results in "java.util.NoSuchElementException: key not found: baz"
I tested it on 2.10, 2.11.8, 2.12.RC2 with same results.
The implicit innocentMap is the culprit.
After decompiling "baz".nonEmpty looks like this:
((TraversableOnce)innocentMap.apply("baz")).nonEmpty();

Can anyone tell me why innocentMap.apply is magically called here?
Highly unexpected behaviour. If innocentMap has different type like:
implicit val innocentMap = Map("foo" -> ("bar"))
code behaves as expected.

Sholud I report scala library bug?

Kevin Wright

unread,
Oct 22, 2016, 10:04:26 AM10/22/16
to scala-language
It’s not a bug… The problem is that you’re making an implicit from a *very* common type!

Strings don’t have a `nonEmpty` method, so when you try to invoke “baz”.nonEmpty, the compiler will look for an implicit conversion to a type that DOES have the method.

In this case, if finds the implicit Map.  This is because Map[A,B] subclasses A => B, and can therefore be used as an implicit conversion!

The conversion is attempted, but the function fails (because the map isn’t defined for “baz”), and you get the error.

The solution?  Don’t make an implicit conversion from Strings like this, especially against such a common type, it’s incredibly risky!



--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-language+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Kevin Wright
mail / hangouts / msn : kev.lee...@gmail.com
vibe / skype: kev.lee.wright

"My point today is that, if we wish to count lines of code, we should not regard them as "lines produced" but as "lines spent": the current conventional wisdom is so foolish as to book that count on the wrong side of the ledger" ~ Dijkstra

Roland Kuhn

unread,
Oct 26, 2016, 8:27:53 PM10/26/16
to scala-l...@googlegroups.com
Hi Gallus,

this is not a bug, although it is not intuitive. One advice that removes a large class of such surprises is that you should never use implicits of primitive types or common collections thereof—the risk for having these picked up in the wrong context is just too high. In this case, since Map extends Function1 your implicit value is not innocent at all, because it presents another way how to turn a String into something that has a nonEmpty method.

One thing that might be considered a bug is that declaring this value does not raise an implicit conversion warning—normally such dangerous activities are protected by a language feature import.

Regards,

Roland

--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-languag...@googlegroups.com.

Jasper-M

unread,
Oct 27, 2016, 3:02:06 AM10/27/16
to scala-language


Op donderdag 27 oktober 2016 02:27:53 UTC+2 schreef rkuhn:
Hi Gallus,

this is not a bug, although it is not intuitive. One advice that removes a large class of such surprises is that you should never use implicits of primitive types or common collections thereof—the risk for having these picked up in the wrong context is just too high. In this case, since Map extends Function1 your implicit value is not innocent at all, because it presents another way how to turn a String into something that has a nonEmpty method.

One thing that might be considered a bug is that declaring this value does not raise an implicit conversion warning—normally such dangerous activities are protected by a language feature import.


I definitely consider that a bug. This is a case where a warning is actually necessary because it is not obvious that you're introducing an implicit conversion, but you don't get a warning. While if you explicitly write an implicit conversion you do get one.

Andrew Phillips

unread,
Oct 27, 2016, 8:52:25 AM10/27/16
to scala-language
Hi Jasper

> This is a case where a warning is actually necessary because it is not obvious that you're introducing an implicit conversion

Would you define "obvious introductions" as being "implicit defs only", or would you include other options too?

Since Scala generally treats defs and values of type Function1 interchangeably, it seems consistent to me to also regard any implicit value of type Function1 as being an "intended" implicit conversion. Or rather, the idea of potentially introducing a distinction between defs and Function1 values for implicits only seems inconsistent.

Should a possible warning in this case only warn on Function1 subtypes, or do you have some other way of marking implicits as "intentional" in mind?

Regards

ap

PS: Another occurrence of this: http://scalapuzzlers.com/#pzzlr-054

Jasper-M

unread,
Oct 27, 2016, 10:47:14 AM10/27/16
to scala-language
Hi Andrew,

It's probably best if a warning is given for all implicit conversions (as I thought was supposed to be the case now, unless you import scala.language.implicitConversions).
But I think it's highly unlikely that an implicit val or def that has a type (explicit or inferred) that is not Function1 but a subtype thereof (like List[A], Map[A,B]) is intended to be an implicit conversion.

So I would define an "obvious implicit conversion" as any implicit whose static type T =:= Function1[A,B] or can be eta expanded to a value with such a type T.


Kinds regards,
Jasper

Op donderdag 27 oktober 2016 14:52:25 UTC+2 schreef Andrew Phillips:

som-snytt

unread,
Nov 5, 2016, 2:45:29 AM11/5/16
to scala-language

I think there is an old discussion about why there's no warning on the val? But I bet a third party lint warning is warranted.

Using def with parens helps:

implicit def innocentMap() = Map("foo" -> Set("bar"))

If I were a rapper, I'd be def with parens. Something about living with my parents.

Andrew Phillips

unread,
Nov 5, 2016, 11:54:13 AM11/5/16
to scala-language
> implicit def innocentMap() = Map("foo" -> Set("bar"))

Somewhat tangential: would using parens for an no-argument function that isn't side-effecting be considered poor style? 


Or is that advice to be interpreted as "drop only if not side-effecting", rather than "drop if and only if ..."?

Regards

ap
Reply all
Reply to author
Forward
0 new messages