Merkle Tree Introduction

The aim of this post is to provide an overview of the basic Merkle Tree data structure and a jumping off point for more advanced topics related to Merkle Trees. While taking the Bitcoin and…

Smartphone

独家优惠奖金 100% 高达 1 BTC + 180 免费旋转




Anatomy of a Scala quirk

This evening I came across this tweet:

which shows one of the most classic “Scala WAT” snippets.

Seasoned Scala developers usually know the answer, but I think it’s easy to underestimate the number of Scala features that play together to produce this spectacularly unintuitive result.

First of all, the answer is "falsed".

Yup. You didn’t misread: it’s exactly the string "falsed".

Let’s see how that’s even possible.

This is a core feature of Scala, although I don’t think it has an official name, so I arbitrarily called it “synthetic apply”. The official spec mentions it as follows:

In other terms, this means that you can take any value f and apply it to some arguments as you would do with a function:

f("some", "arg")

The compiler will automatically treat it as:

f.apply("some", "arg")

provided that the value f has a method apply defined.

In our example, this means that List("a", "b", "c").toSet() + "d"gets interpreted as:

List("a", "b", "c").toSet.apply() + "d"

“But why? toSet is a method, so why isn’t it called directly instead of inserting .apply?”

Good question! It turns out that Scala allows “nullary” or “parameterless” methods, i.e. methods that don’t have a parameter list. This is different than methods that have an empty parameter list!

You can’t pass parameters (not even empty) to a nullary method, so this won’t compile:

If you squint really hard, you’ll see our friend .apply at work there: nullaryMethod is already an Int before you add () to . Adding () causes the compiler to try to “call” Int, but that doesn’t make sense because… Int does not take parameters.

Back to our example, you can verify that the methodtoSet of List is a nullary method. So when we’re adding (), we are already dealing with a Set.That’s why the compiler inserts .apply for us.

What does .apply of Set do? Reading the documentation we discover that it is an alias for contains.

Fair enough, we have written a convoluted and unexpected version of contains .

But what’s the element we’re testing against? The parameter list is empty and apply clearly takes one parameter. That MUST be a compiler error, right?

The code above — unfortunately — compiles, albeit with a deprecation warning starting from Scala 2.12.

The feature you’re looking for is called something along the lines of “adapted arguments”, and often divided in two: “auto-tupling” and “unit insertion”. This feature is not in the spec (as far as I know) and it’s implementation-defined. This feature can automatically adapt arguments in case they don’t precisely match their arity.

For example:

This has even more surprising consequences when the parameters are missing:

In the last line, the compiler decided that we truly wanted to use ()(the literal value for Unit), but we felt too lazy to write mirror(()) , so it inserted the () for us, and inferred Unit for the A type.

Back to our example, it’s now clear that we’re doing:

List("a", "b", "c").toSet.apply(()) + "f"

For the record, List("a", "b", "c") does not contain any element () so the result of that expression is false.

Oh, by the way, if you’re wondering why we can check that a Unit does not belong to a Set[String] (which should rightfully be a type error), the answer is that we’re not really dealing with Set[String] at any point.

toSet is defined as def toSet[B >: A]: Set[B] , which allows for a List[A] to become a Set[B] as long as B is supertype of A . The problem with our example is that the inference engine postpones the decision about what B should be until we hit on thatapply(()) , at which point we’re trying to unify String and Unit, resulting in a glorious Any ! You can verify this yourself with a REPL:

Let’s then annotate the proper types:

List("a", "b", "c").toSet[Any].apply(()) + "d"

which ultimately results in

false + "d"

The mystery is almost solved, but we’re left with one final question: why can we “sum” a Boolean and a String? Do all values in Scala define a magical + method that accepts a String as a parameter?

Well, almost… The standard library defines an implicit class called any2stringadd which adds that + method on literally any type, which will happily call String.valueOf (a null-safe version of toString)

(Note that this is different from the + method defined on String, which doesn’t require implicit conversions)

To wrap it up, this is our nasty snippet in its full glory:

new any2Stringadd(List("a", "b", "c").toSet[Any].apply(())) + "d"

which (obviously!) results in the string "falsed".

If programming languages quirks terrify you, probably.

Jokes aside, every programming language has its scary and frankly hilarious bits, and this shouldn’t automatically throw us off.

I’ve been a Scala programmer for 5 years, and I’ve never encountered anything like this in the wild, although some features of the languages will eventually bite you if you combine enough of them.

On the bright side, it looks like this specific example will be even harder to reproduce in the next versions of Scala:

In the meantime, I suggest to at least enable the compiler flag -Yno-adapted-argsto get compiler errors on auto-tupling and unit insertion.

So, let’s be positive! And — if anything — you can now tell your fellow Scala developers a funny Scala quirk and explain it to them ;-)

I hope you’ve enjoyed this impromptu blog post!

Add a comment

Related posts:

Democrat Politicians are the Hypocrites

This is not Republican hypocrisy. It is common sense. Guns are not required in facilities where armed guards and security is provided. All most conservatives and libertarians are saying is that…

Battle Racers joins the Atari Token Gaming Competition!

Arcade racing blockchain game Battle Racers enables support for Arkane Network and participates in a giveaway for the newly-launched Atari token.

I love Narwhals and you should too

The world has just been newly created and Adam and Karen are roaming around admiring all the other creatures with wonderous eyes. ”All these animals are so beautiful that Lord so thoughtfully put…