38

I understand the difference between zero-parameter and parameterless methods, but what I don't really understand is the language design choice that made parameterless methods necessary.

Disadvantages I can think of:

  • It's confusing. Every week or two there are questions here or on the Scala mailing list about it.
  • It's complicated; we also have to distinguish between () => X and => X.
  • It's ambiguous: does x.toFoo(y) mean what it says, or x.toFoo.apply(y)? (Answer: it depends on what overloads there are x's toFoo method and the overloads on Foo's apply method, but if there's a clash you don't see an error until you try to call it.)
  • It messes up operator style method calling syntax: there is no symbol to use in place of the arguments, when chaining methods, or at the end to avoid semicolon interference. With zero-arg methods you can use the empty parameter list ().

Currently, you can't have both defined in a class: you get an error saying the method is already defined. They also both convert to a Function0.

Why not just make methods def foo and def foo() exactly the same thing, and allow them to be called with or without parentheses? What are the upsides of how it is?

Luigi Plinge
  • 50,650
  • 20
  • 113
  • 180
  • 4
    You didn't even mention inheritance rules and interaction with vals. Oh, and the lack of symmetry with by-name parameters, which could be the one saving grace (which themselves also convert to `Function0`). – Rex Kerr Sep 08 '12 at 22:46
  • You are mixing up two completely different things: no-parens versus empty-parens _methods_ (as the title suggests), and `Function0` versus thunks (as your bullet list suggests). – 0__ Sep 09 '12 at 08:48
  • possible duplicate of [Why to use empty parentheses in Scala if we can just use no parentheses to define a function which does not need any arguments?](http://stackoverflow.com/questions/3877953/why-to-use-empty-parentheses-in-scala-if-we-can-just-use-no-parentheses-to-defin) – 0__ Sep 09 '12 at 08:51
  • @0__ The questions are totally different. That one asks when we should use one or the other. This one asks why there needs to be both when there is (possibly - what I'm asking) a viable alternative of making them exactly the same. I'm pretty sure it's not to support a naming convention, which wouldn't make sense anyway since empty-paren methods can be called with or without the parens (and could also be defined with or without them). – Luigi Plinge Sep 09 '12 at 09:28
  • @LuigiPlinge - ok, they might not be the same (but related). I tried to add an answer, thus. – 0__ Sep 09 '12 at 09:30

5 Answers5

31

Currying, That's Why

Daniel did a great job at explaining why parameterless methods are necessary. I'll explain why they are regarded distinctly from zero-parameter methods.

Many people view the distinction between parameterless and zero-parameter functions as some vague form of syntactic sugar. In truth it is purely an artifact of how Scala supports currying (for completeness, see below for a more thorough explanation of what currying is, and why we all like it so much).

Formally, a function may have zero or more parameter lists, with zero or more parameters each.
This means the following are valid: def a, def b(), but also the contrived def c()() and def d(x: Int)()()(y: Int) etc...

A function def foo = ??? has zero parameter lists. A function def bar() = ??? has precisely one parameter list, with zero parameters. Introducing additional rules that conflate the two forms would have undermined currying as a consistent language feature: def a would be equivalent in form to def b() and def c()() both; def d(x: Int)()()(y: Int) would be equivalent to def e()(x: Int)(y: Int)()().

One case where currying is irrelevant is when dealing with Java interop. Java does not support currying, so there's no problem with introducing syntactic sugar for zero-parameter methods like "test".length() (which directly invokes java.lang.String#length()) to also be invoked as "test".length.

A quick explanation of currying

Scala supports a language feature called 'currying', named after mathematician Haskell Curry.
Currying allows you to define functions with several parameter lists, e.g.:

def add(a: Int)(b: Int): Int = a + b
add(2)(3) // 5

This is useful, because you can now define inc in terms of a partial application of add:

def inc: Int => Int = add(1)
inc(2) // 3

Currying is most often seen as a way of introducing control structures via libraries, e.g.:

def repeat(n: Int)(thunk: => Any): Unit = (1 to n) foreach { _ => thunk }

repeat(2) {
  println("Hello, world")
}

// Hello, world
// Hello, world

As a recap, see how repeat opens up another opportunity to use currying:

def twice: (=> Any) => Unit = repeat(2)

twice {
  println("Hello, world")
}

// ... you get the picture :-)
nadavwr
  • 1,820
  • 16
  • 20
  • Do you mean "method" where you say "function" throughout? – Josiah Yoder Jun 24 '16 at 18:18
  • 1
    Scala draws a technical distinction between 'function objects' and 'methods', but at the broader sense both are used to describe functions. Currying, as a concept, is defined over functions—hence the interchangeable use of the terms 'method' and 'function'. – nadavwr Jun 24 '16 at 18:38
  • 1
    Thanks. Can you also curry on Scala's 'function objects'? – Josiah Yoder Jun 24 '16 at 18:40
  • 1
    Sure: ```val add: Int => Int => Int = _ + _; val increment: Int => Int = add(1)``` – nadavwr Jun 24 '16 at 18:43
  • Can you make a zero-parameter list 'function object'? – Josiah Yoder Jun 24 '16 at 19:06
  • 1
    No—those are implementations of the Function* traits, which all have a single parameter list. The only language constructs in scala intended to encode zero parameter-list functions distinctly from zero-parameter functions are, to my knowledge, methods and parameters. – nadavwr Jun 24 '16 at 19:28
13

One nice thing about an issue coming up periodically on the ML is that there are periodic answers.

Who can resist a thread called "What is wrong with us?"

https://groups.google.com/forum/#!topic/scala-debate/h2Rej7LlB2A

From: martin odersky Date: Fri, Mar 2, 2012 at 12:13 PM Subject: Re: [scala-debate] what is wrong with us...

What some people think is "wrong with us" is that we are trying bend over backwards to make Java idioms work smoothly in Scala. The principaled thing would have been to say def length() and def length are different, and, sorry, String is a Java class so you have to write s.length(), not s.length. We work really hard to paper over it by admitting automatic conversions from s.length to s.length(). That's problematic as it is. Generalizing that so that the two are identified in the type system would be a sure way to doom. How then do you disambiguate:

type Action = () => () def foo: Action

Is then foo of type Action or ()? What about foo()?

Martin

My favorite bit of paulp fiction from that thread:

On Fri, Mar 2, 2012 at 10:15 AM, Rex Kerr <ich...@gmail.com> wrote:

>This would leave you unable to distinguish between the two with 
>structural types, but how often is the case when you desperately 
>want to distinguish the two compared to the case where distinguishing 
>between the two is a hassle?


/** Note to maintenance programmer: It is important that this method be
 *  callable by classes which have a 'def foo(): Int' but not by classes which
 *  merely have a 'def foo: Int'.  The correctness of this application depends
 *  on maintaining this distinction.
 *  
 *  Additional note to maintenance programmer: I have moved to zambia.
 *  There is no forwarding address.  You will never find me.
 */
def actOnFoo(...)

So the underlying motivation for the feature is to generate this sort of ML thread.

One more bit of googlology:

On Thu, Apr 1, 2010 at 8:04 PM, Rex Kerr <[hidden email]> wrote: On Thu, Apr 1, 2010 at 1:00 PM, richard emberson <[hidden email]> wrote:

I assume "def getName: String" is the same as "def getName(): String"

No, actually, they are not. Even though they both call a method without parameters, one is a "method with zero parameter lists" while the other is a "method with one empty parameter list". If you want to be even more perplexed, try def getName()(): String (and create a class with that signature)!

Scala represents parameters as a list of lists, not just a list, and

List() != List(List())

It's kind of a quirky annoyance, especially since there are so few distinctions between the two otherwise, and since both can be automatically turned into the function signature () => String.

True. In fact, any conflation between parameterless methods and methods with empty parameter lists is entirely due to Java interop. They should be different but then dealing with Java methods would be just too painful. Can you imagine having to write str.length() each time you take the length of a string?

Cheers

som-snytt
  • 39,429
  • 2
  • 47
  • 129
11

First off, () => X and => X has absolutely nothing to do with parameterless methods.

Now, it looks pretty silly to write something like this:

var x() = 5
val y() = 2
x() = x() + y()

Now, if you don't follow what the above has to do with parameterless methods, then you should look up uniform access principle. All of the above are method declarations, and all of them can be replaced by def. That is, assuming you remove their parenthesis.

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • 1
    OK, after reading this 8 times and thinking about it for 10 minutes, I think I get what you're saying! It's so that if we have a `def toFoo` we could for example override it with `val toFoo`, which you can't access with `toFoo()`. One solution could be to make parens optional for vals/vars just as they are for methods. – Luigi Plinge Sep 09 '12 at 10:48
  • 2
    This would look silly, but is it any sillier than that `val b = 5` can override `def b(): Int`, and that the latter but not the former will match `B <: { def b(): Int }`, unless you cast the subclass back to the superclass? LSP seems to have fallen by the wayside here. – Rex Kerr Sep 09 '12 at 15:27
  • 1
    @LuigiPlinge It's more than subclassing a `def` with a `val` -- it's the very ability to rewrite a class that used a `var` or `val` with `def`, changing the implementation without breaking even binary compatibility. – Daniel C. Sobral Sep 10 '12 at 01:44
  • 1
    Here's a tutorial on Scala's implementation of the [uniform access principle](http://joelabrahamsson.com/learning-scala-part-nine-uniform-access/) that I found helpful. – Josiah Yoder Jun 24 '16 at 18:32
8

Besides the convention fact mentioned (side-effect versus non-side-effect), it helps with several cases:

Usefulness of having empty-paren

// short apply syntax

object A {
  def apply() = 33
}

object B {
  def apply   = 33
}

A()   // works
B()   // does not work

// using in place of a curried function

object C {
  def m()() = ()
}

val f: () => () => Unit = C.m

Usefulness of having no-paren

// val <=> def, var <=> two related defs

trait T { def a:   Int; def a_=(v: Int): Unit }
trait U { def a(): Int; def a_=(v: Int): Unit }

def tt(t: T): Unit = t.a += 1  // works
def tu(u: U): Unit = u.a += 1  // does not work

// avoiding clutter with apply the other way round

object D {
  def a   = Vector(1, 2, 3)
  def b() = Vector(1, 2, 3)
}

D.a(0)  // works
D.b(0)  // does not work

// object can stand for no-paren method

trait E
trait F { def f:   E }
trait G { def f(): E }

object H extends F {
  object f extends E  // works
}

object I extends G {
  object f extends E  // does not work
}

Thus in terms of regularity of the language, it makes sense to have the distinction (especially for the last shown case).

0__
  • 66,707
  • 21
  • 171
  • 266
  • You've shown what works and what doesn't work in the language as it is, but what limitations would there would be if they were hypothetically both the same? In your case I, it would not need to be a problem because there's no distinction. – Luigi Plinge Sep 09 '12 at 10:51
  • (1) The main limitation would be aesthetics: ugliness and irregularity. (2) You propose to treat empty-paren and no-paren equally and allow both to be called both ways. It can't work because you create an ambiguity, unless you remove the possibility to collapse `apply` in Scala (remember this is used for case classes, collections, etc.). If, in the last example, `F` was defined as `trait F { def apply: E }`, what would `H.f` or `H.f()` mean? You could not tell whether they should return an `E` or `F`. – 0__ Sep 09 '12 at 11:12
  • 1
    Scala already doesn't collapse `apply` or `apply()`. You need parens to collapse the latter, and the former never collapses. By-name parameters are not what you get from `f _` where `def f: F` has no parens, and that would be the way to be consistent with currying. Only your `D` example actually shows anything, and then you can always assume no parens, and try reparsing if that assumption is wrong. – Rex Kerr Sep 09 '12 at 18:31
  • @Rex - `object Q { def apply[A] = 33 }; Q[Int]` = collapses. Please see the last sentence of my comment; would you want to use a heuristic here for a given expected return type, and throw an ambiguity error in the case of `val x = H.f`? – 0__ Sep 09 '12 at 23:47
  • `object R { def apply[A]() = 33 }; R[Int]` also collapses, and your `Q` example without a type parameter does not call apply. Are you sure you remember the existing behavior? – Rex Kerr Sep 10 '12 at 07:51
4

I would say both are possible because you can access mutable state with a parameterless method:

class X(private var x: Int) {
  def inc() { x += 1 }
  def value = x
}

The method value does not have side effects (it only accesses mutable state). This behavior is explicitly mentioned in Programming in Scala:

Such parameterless methods are quite common in Scala. By contrast, methods defined with empty parentheses, such as def height(): Int, are called empty-paren methods. The recommended convention is to use a parameterless method whenever there are no parameters and the method accesses mutable state only by reading fields of the containing object (in particular, it does not change mutable state).

This convention supports the uniform access principle [...]

To summarize, it is encouraged style in Scala to define methods that take no parameters and have no side effects as parameterless methods, i.e., leaving off the empty parentheses. On the other hand, you should never define a method that has side-effects without parentheses, because then invocations of that method would look like a field selection.

Community
  • 1
  • 1
kiritsuku
  • 52,967
  • 18
  • 114
  • 136
  • 7
    This is a convention only. The compiler doesn't help enforce this at all, which makes it only slightly better than useless. (Worse than useless if you count on it.) – Rex Kerr Sep 08 '12 at 22:47
  • 1
    It would make (slightly) more sense if parentheses for empty-paren methods were enforced, but since you can elide them, this convention can't be the reason for including parameterless methods in the spec. – Luigi Plinge Sep 08 '12 at 23:18