Pre-SIP ideas for by-name exlicit syntax for caller

Hello,
In practice there is no syntactical difference between function arguments that are T and those that are Function0[T]. This makes it difficult for a human to understand if an argument is evaluated more than once. I would like to change that, although I am not sure of the best way.

A trivial solution is be rid of by-name syntax.

f(arg)

becomes

f(() => arg)

Another solution would be to require special syntax for by-name parameters. An example would be to require =>, so that

f(arg)

becomes

f(=> arg)

This would make it so that Function0[T] and Function1[Unit, T] have different surface syntax. => arg and arg would type differently, and so the compiler can help find problems when the author meant one versus another.

Additionally,

val x: Function0[Int] = 3

becomes

val x: Function0[Int] = => 3

This example makes me think my first idea is better, but it’s only because () would break up the two =s. I don’t think Function0 is often used this way. However, there is an upside!

val x = => 3

would be inferred as a Function0.

Please respond with any other ideas or criticisms!

Thanks,
Jeff

Hey @shawjef3, thanks for opening a discussion around this.

Generally, I don’t think this is a huge problem.

I think by-name parameters are useful as they are – they are syntactic sugar for Function0. I am not aware of any evaluation semantics difference between the two. The syntactic changes you propose would make the use of by-name parameters difficult and clunky, which contradicts the idea of having syntactic sugar for the use of parameterless functions.

However, I think there’s some room for improvement.

As by-name parameters is not a common concept in other programming languages, Scala developers have to learn it when they bump into it (it’s difficult to guess what they are from just seeing it in the wild). If by-name parameters semantics are not intuitive, it’s an education problem – something that we should address in the docs and Scala learning material.

I guess this refers to the fact that by-name parameters are executed every time there is a reference to it from the method body. I had a casual discussion with @odersky to consider changing it so that by-name parameters are only executed once. However, I do not remember what Martin told me in response, so I cannot clarify whether that is possible or not. This change seems to go against the real semantics of named parameters. However, I think that changing the semantics would be better than what you propose. Do you agree?

However, I am aware of several places that depend on these semantics of by-name. For example, I remember seeing some Scala standard library code relying on this. Breaking this code is something that, if done, has to be done with care, and probably in a major version of Scala (2.13.x or Dotty).

Honestly, the cure feels far worse than the disease on this one. While I can see the objection in principle, in practice I don’t recall ever encountering it, and by-name usage is sufficiently common (and useful for keeping call-site code clean) that I would strongly object to making it wordier.

@jvican’s idea of changing the use-side to (effectively) treat the result as a lazy val, to avoid double-calls, is intriguing; that would reify in the language what I generally think of as good practice anyway. I’m curious how much code depends on multiple executions of the call-by-name function.

1 Like

The thing is, with the current semantics you can always do

def foo(f: => Unit) = {
  lazy val f0 = f
  ???
}

If you turn it around this gets a bit cleaner, but code that requires reevaluation becomes impossible without making the calls look like foo(() => ???). In that case I prefer cleaner calls over cleaner definition.
By-name parameters in Scala are mostly about clean callsites anyway. Otherwise you could always use normal functions.

2 Likes

@shawjef3 Thanks for pointing out that this isn’t clear. I will update the documentation accordingly in the next week: http://docs.scala-lang.org/tutorials/tour/by-name-parameters.html

Hello,

I think the idea was not so much enabling the implementer to have it
evaluate only once, but to have a guarantee for the caller that it is only
evaluated once.

 Best, Oliver

One more or less implies the other, I guess. In what @jvican was saying anyway.

My intent is not to change the semantics of by-name parameters, but to require by-name parameters be explicitly Function0[T].

In fact, after this discussion, I should change the focus of my original post.

The real problem, as I see it, is automatic conversion from T to Function0[T]. It is done silently whenever a Function0 is required. Some explicit syntax should be required, just as it is for, say, Function[Unit, T].

But that problem doesn’t exist, as far as I know.

scala> def foo(a: Function0[Int]) = a()
foo: (a: () => Int)Int

scala> foo(42)
<console>:13: error: type mismatch;
 found   : Int(42)
 required: () => Int
       foo(42)
           ^

I thought that by-name parameters were Function0. I guess they’re a different beast, which is even more confusing.

Something that is evaluated when used should really be a Function, and not a value. So I suppose there needs to be yet another type, something like ByName[T], which has the syntax sugar => T.

Under the hood they are implemented as Function0. I think what you actually meant was probably “remove by-name parameters”.

1 Like

I think you’re right, Jasper-M. If by-name parameters were removed, they could be replaced by explicit Function0s, and => could be introduced as syntax for Function0.

I’m nut sure if suggested solution suits well to the scala language, but I encounter the described problem regularly. Not too often though. It arises when I study a library from its usage documentation instead of scaladocs. In certain cases order of evaluation matters much. So I go to REPL, create simple example and test if the library function uses call-by-name semantics. It would be great to distinguish call-by-name without constructing test examples or looking inside the source code. And it would be equally miserable to lost nice DSL syntax method_name { } to create Future, Try and so on.

casper - if the DSL syntax became something like method_name => { } is that okay? It looks awkward because we’re not used to it, but it shows you that the parameter is a Function0.

This variant is definitely no ok, because it reserved for closure over method_name variable.

This came up on chat recently somewhere. If f(A) is overloaded with f(=> A), there is no syntax for explicitly selecting the by-name version. (Maybe the overload ought to be disallowed.)

The question posed here could be expressed the other way: explicit syntax to ensure eval-once semantics.

Instead of f(launch()), something with a lazy val as Jasper-M pointed out. f(lazy launch()) would say eval at most once no matter how f is defined.

Other syntaxes where people want something evaluated in a handy scope and order are early definitions and leading midstream assignment in for comprehensions.

for (lazy x = launch()) f(x) or for (lazy x = launch() ; ok <- f(x)) yield ok

or without the lazy for exactly-once.

som-snytt, how about if ‘lazy’ became syntax for creating a Function0, and we remove by-name parameters?

val x = lazy {}
f(x) //calls Function0[A] variant, i.e. by-name overload

vs

val x = {}
f(x)  //calls A variant, i.e. concrete overload

where {} is some block that returns an A.

On second thought, “lazy” probably isn’t the best keyword, since it implies that the block will be evaluated only once. Anyone disagree or have another idea?

Maybe “thunk”, if we want to be Haskell-ey.

“Thunk” isn’t any better, since they’re only evaluated once.

How about “yield”? A “yield” without a “for” would be a Function0.

val x = yield println("hi")
1 Like