Getting rid of use-site method-call-parens-count?

@ def foo()()()()() = 1
defined function foo
@ foo
res1: Int = 1
@ foo()
res2: Int = 1
@ foo()()
res3: Int = 1
@ foo()()()()()
res4: Int = 1
@ foo()()()()()()
cmd5.sc:1: Int does not take parameters
val res5 = foo()()()()()()
                        ^
Compilation Failed
@

This is something that has annoyed me about Scala since forever, and is a major contributor to my initial confusion learning the language (Nine ways to define a method in Scala?).

We like to think Scala’s declaration-site variance is better than Java’s confusing use-site variance, perhaps for good reason. Here we have our own use-site method-call-parens-count, where most other languages (Python, Javascript, C#, …) all have declaration-site method-call-parens-count, where the number of parens you pass to call a function is fixed when you define a function.

I only know of Ruby which lets you include-or-leave-out parentheses at-will when calling methods, which probably isn’t a good example for a statically-typed language to follow…

Put another way, in the above example, foo is an

  • Int
  • () => Int
  • () => () => Int
  • () => () => () => Int
  • () => () => () => () => Int
  • () => () => () => () => () => Int

All at the same time! And Scala is supposed to be a strongly-typed language…

Does anyone actually like this behavior? Is there a chance that we could get rid of it in Dotty?

9 Likes

You make a good argument in my opinion.

I do think that the ability to omit the final argument list when there are implicit parameters is important. If def x(a: A)(implicit b: B) had to be invoked as x(a)(), it would break a lot of Scala’s DSL capabilities.

Other than the special case of implicits, I also like the ability to call legacy Java methods without parentheses. It would be annoying not to be able to use Scala style with Java objects (e.g., (s: String).length).

Other than these two specific cases, I don’t see a lot of benefit to being able to leave off parentheses. I would probably prefer if a method name without parens was always treated as a function object and we didn’t have to do (f _)—although this one change might not be enough to eliminate that need.

2 Likes

Actually, x(a)() does not compile. Implicits only work if you leave off the
whole implicit parameter list.

1 Like

There’s the persistent problem that then you’d have to write x.toString() and I don’t think anybody would want to do that.

We had at some point an intermediate position that only Java defined methods would have optional () arguments. The argument against that (and for full generalization) was that then nobody would be able to convert Java code to Scala without fear that clients would break.

2 Likes

How do you define “convert java code to scala code”? Why wouldn’t you
convert String toString() { return “”; } to def toString = “” without
parentheses?

Hello,

scala> trait A { def m(i:Int, s:String); def m(i:Int)(s:String) }
:11: error: double definition:
def m(i: Int,s: String): Unit at line 11 and
def m(i: Int)(s: String): Unit at line 11
have same type after erasure: (i: Int, s: String)Unit
trait A { def m(i:Int, s:String); def m(i:Int)(s:String) }
^

If in Scala methods with different number of argument lists are identical
as long as the total argument list is the same, then of course def m and
def m() must be the same, too.

 Best, Oliver

Why wouldn’t you convert String toString() { return “”; } to def toString = “” without
parentheses?

That would also risk breaking client code, now because you would no longer accept (). But maybe idioms of when to write () or not are more predictable now than they were then, so it’s less of an issue. I am open to the idea of doing this. We could try it out and see how much breaks.

The main problem to me seems that you (the programmer) have to know at the call site which method is defined in Scala and which in Java.

When you can override toString() with toString it becomes even more confusing cause one time you have to/can add the () and the other time you can’t.

The main problem to me seems that you (the programmer) have to know at the call site which method is defined in Scala and which in Java.

Exactly. It would only work if there are rigid conventions already established, so one could convert with little fear of breaking. E.g. does everybody use x.toString without parentheses but iterator.next() with parentheses? Not sure how far we are on that axis. The other new aspect is that with Scalafix we could maybe rely on automatic rewriting, if a migration strategy is worked out.

How about an annotation to control this?
By default, parens would be optional on Java methods and Scala methods that override/implement Java. On pure Scala methods, parens would be mandatory but this requirement could be relaxed:

@optionalParens
def methodConvertedFromJava(): Unit = ???
1 Like

I think breaking client code I think is fine; procedure syntax and other clean-ups break client code too. Fixing this shouldn’t be any harder than any of the fixes Scalafix is already doing, if I’m not mistaken.

There already is a lot of inconsistency as-is: I always mix up methods defined with def foo() and methods defined as def foo, and sometimes wonder why I can add () to one and not add () to the other, but can call both without ()s. I get puzzled by this relatively often, even in my own code.

An annotation like @ghik mentioned would work too. Automatically apply it to Java code; let anyone in Scala land use it if they wish (though I doubt anybody will).

I honestly don’t see the big deal of calling .toString() with parens. Java developers have been doing it for decades, and I don’t think it’s even in the top 100 problems I (personally) have with Java.

I can see the upside of having Java getters look like properties, but I don’t think that alone is worth spreading such weird behavior throughout the rest of the language.

Removing this feature would be one more step in shrinking the differences between methods and functions, which currently I think have far more ad-hoc differences than is really necessary.

11 Likes

Why not add a parameter-less show method to Scala’s Any type, so that it’s called without parenthesis? We already have ## and == that alias respectively hashCode and equals with more Scala-esque names.
I think it would be easy to define a (purely syntactic) Scalafix rule to transform argument-less calls to toString into calls to show.
We’re still stuck with things like xs.length(), but similarly, with the Java conversions implicit we can call xs.size instead. I think it’s better than having special cases in the language itself (by this I also mean the annotation-based proposal), which are surprising to newcomers.

I agree. Most Java APIs are not going to look idiomatic anyway, so I don’t see the big deal with adding the extra parentheses. If I really want something to look like Scala I’ll create an implicit Scala wrapper for the class that has x.foo instead of x.getFoo(), etc.

1 Like

What about classes that already have a show method defined (with the same or different signature)?

You’re right that it would probably be too much of a breaking change.
The only way I see this could be mitigated would be to choose a (probably symbolic) name that is less likely to be already used, like hashCode's ##, but that doesn’t seem like a good solution either (cf. symbolic names are not such a good idea).

So it’s probably better to just let users come up with implicit wrappers themselves as @gmethvin said, and maybe provide one in the Scala library (similar to -> for tuples).

1 Like

For what it’s worth, I have no problem with every method requiring at least one, possibly empty parameter list. That way there’s never any confusion about when a use of a method or val is a method or val, and the compiler will fail when you think you’re using a val, but actually it’s a method. Likewise, if you think you’re using a method and add parens, but actually it’s a value, the compiler will fail. I see this as a good thing.

Wouldn’t that break scala’s Uniform Access Principle ?

src Glossary | Scala Documentation

1 Like

And similarly a var x can be changed into a def x and def x_= couple, which is very useful IMHO. It completely sidesteps the problem normally solved in Java with tons of boilerplate, i.e. “always make accessors and never let mutable variables be public, because who knows if you need to change or override their behavior later”.

Wouldn’t that break scala’s Uniform Access Principle ?

Scala’s implementation of Uniform Access Principle is already broken, as demonstrated by lihaoyi. My opinion is that it should be removed or fixed, but not left as-is.

But fixing the problem described by @lihaoyi does not require forcing methods to have at least one parameter list (which is what you suggest). What your suggestion is doing is to break the UAP. But the UAP is something completely orthogonal to the problem discussed here.