It doesn’t change the semantics, only the side-effects surrounding initialization. “But I mean for initialization side-effects to be semantics!” you might say. Well…maybe?
I agree that it’s a bit surprising. But on the other hand, suppose you have
object Bar:
println("Bar!")
inline def goodbye = "goodbye"
Suppose you call Bar.goodbye
. Do you really expect Bar
’s initializer to run? If yes, how the heck do you actually just inline pure code? All inline code is in something. “To use inline code, you must create objects” seems completely opposite to the intent of inline code.
What about this one:
scala> lazy val y =
| println("Hey")
| 47
|
lazy val y: Int
scala> inline def ignore[A](a: A): Unit =
| println("I totally ignored my input")
|
def ignore[A](a: A): Unit
scala> ignore(y)
I totally ignored my input
That seems sensible enough! You didn’t use y
at all, so why would you initialize it?
And if you “have” something of the right type, but it happens to be null, but you don’t use it, who cares? And indeed, who cares:
scala> val a = (null: Foo).hello
val a: String = hello
The question really is whether one views initialization as incidental to performing the operation if the operation does not depend on it (in which case it can be elided as a performance optimization), or whether one views initialization as part of the “same operations” as required by “same semantics”.
Personally, I think the current behavior is better. People will be surprised either way, because at different times they’ll think, “But of course I care about initialization!” or, “But of course initialization is an irrelevant detail to skip if possible!”
In terms of writing efficient, flexible code (without which one wouldn’t bother with inline
at all), the current behavior is, I think, superior.