Wat? Scala

As I got quoted recently in “Scala — 1★ Would Not Program Again” I though I finally should write up a little Wat moment we had recently:

So does anyone know “wat” the following Scala code returns? (Value and Type)

List(1,2,3).toSet()

A Set<Int> containing 1,2,3?

Nope how about I give you a clue, there are 2 bugs in this one line:

  1. A type inferencing bug where it chooses Set<Any>
  2. A bug where the brackets are used for both calling the Set.apply method and constructing Unit, notice there no space between the “toSet” and “()”

Yup you guessed it, it returns:

false

Wat? Try it in your repl and for even more fun check the bytecode out.

UPDATE:
Looks like (“-Yno-adapted-args”, “Do not adapt an argument list (either by inserting () or creating a tuple) to match the receiver.”) is your friend

Pete Kneller has done some really good analysis so you can see all the different weird combinations

Leave a comment ?

10 Comments.

  1. Not an issue since the type safety of the language will save you.

  2. Not sure what type inferencing bug you are referring to, but for me (2.9.2) this returns a Set[Int]:

    List(1,2,3).toSet
    res3: scala.collection.immutable.Set[Int] = Set(1, 2, 3)

    Also List(1,2,3).toSet() isn’t a bug. “toSet” is defined as “def toSet” since it does not have any side effects. It’s not the clearest feature but I don’t think it’s a bug.

  3. The idiomatic call works just fine:
    scala> List(1,2,3).toSet
    res2: scala.collection.immutable.Set[Int] = Set(1, 2, 3)

    just don’t clutter your code with unnecessary parantheses.

    What has happend here?

    1. there is no method “toSet()” defined on List just “toSet”
    2. What you did was call
    List(1,2,3).toSeq.apply()
    3. The apply method of a set returns true if the element you call for exists i the set
    4. () can’t exist

  4. Hideous behaviour, I don’t want to make excuses for it. Just wanted to point out that adding the -Yno-adapted-args to the scalacOptions property will prevent this from happening.

  5. Welcome to Scala version 2.10.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_21).
    Type in expressions to have them evaluated.
    Type :help for more information.

    scala> List(1,2,3).toSet()
    :8: warning: Adapting argument list by inserting (): this is unlikely to be what you want.
    signature: GenSetLike.apply(elem: A): Boolean
    given arguments:
    after adaptation: GenSetLike((): Unit)
    List(1,2,3).toSet()
    ^
    res0: Boolean = false

    It’s not like it doesn’t warn you that bad things are happening.

  6. -Yno-adapted-args

    Thanks that’s really good to know, hopefully they will remove this behaviour completely real soon.

  7. Hi Dan,

    nice post! It’s an issue with auto-tupling, where the compiler tries to be too helpful.

    The toSet method doesn’t take any parameters, so the compiler assumes that you must have meant to call the apply method of Set, and that method returns a Boolean.

    So what the compiler understands looks more like
    List(1,2,3).toSet.apply(())

    Some discussions about this on the mailing list: https://groups.google.com/d/topic/scala-internals/4RMEZGObPm4/discussion https://groups.google.com/d/topic/scala-debate/zwG8o2YzCWs/discussion

    If everything works out, we can hopefully get rid of it for 2.11. :-)

    Bye,

    Simon

  8. Hi Dan,

    automatic () insertion will be deprecated for 2.11 and most likely removed in 2.12.

    The corresponding tickets in the bug tracker are:

    https://issues.scala-lang.org/browse/SI-8035
    https://issues.scala-lang.org/browse/SI-8036

    Thanks,

    Simon

  9. x and x() are different signatures in scala, and the convention is that you use x() for things that have side effects. but that’s not even massively relevant here, just rtfm.

  10. The link to Pete’s gist is broken, the new link is https://gist.github.com/petekneller/7803974

    ttfn

Leave a Comment