64

So I've been trying to puzzle through the various ways you can define stuff in Scala, complicated by my lack of understanding of the way {} blocks are treated:

object NewMain extends Thing{

    def f1 = 10
    def f2 {10}
    def f3 = {10}
    def f4() = 10
    def f5() {10}
    def f6() = {10}
    def f7 = () => 10
    def f8 = () => {10}
    def f9 = {() => {10}}

    def main(args: Array[String]){
        println(f1)     // 10
        println(f2)     // ()
        println(f3)     // 10
        println(f4)     // 10
        println(f4())   // 10
        println(f5)     // ()
        println(f5())   // ()
        println(f6)     // 10
        println(f6())   // 10
        println(f7)     // <function0>
        println(f7())   // 10
        println(f8)     // <function0>
        println(f8())   // 10
        println(f9)     // <function0>
        println(f9())   // 10
    }

}

Presumably some of these are equivalent, some of these are syntactic sugar for others, and some are things I should not use, but I can't for the life of me figure it out. My specific questions are:

  • How is it that println(f2) and println(f5()) gives unit? Isn't the last item in the block 10? How is it different from println(f3()), which gives 10?

  • If println(f5) gives unit, shouldn't println(f5()) be invalid, since unit is not a function? The same applies to println(f6) and println(f6())

  • Of all the ones which print 10: f1, f3, f4, f4(), f6, f6(), f7(), f8(), f9(), is there any functional difference between them (in terms of what it does) or usage differences (in terms of when I should use which)? Or are they all equivalent?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Li Haoyi
  • 15,330
  • 17
  • 80
  • 137
  • 6
    I think the title is a bit misleading because it suggests that there ***are*** 9 ways to define a method. There aren't. It's like saying you can find `a + b` in 57 ways: `(a + b)`, `a; b`, `(a) + b`, `a + (b)`, `(a + (b))`, `(a) - b`... some of which are wrong, all of which are needlessly complex. – Luigi Plinge Nov 29 '11 at 16:34
  • 4
    Ironically,`f10` was the correct way. – som-snytt Jan 26 '14 at 01:10

4 Answers4

36

To answer your questions in order:

  • f2 and f5() return Unit because scala takes any def without an "=" to be a function that returns Unit, regardless of what the last item in a block is. This is a good thing, since otherwise it would not be rather verbose to define a function that does not return anything.
  • println(f5()) is valid, even though it returns Unit because in scala Unit is a valid object, though admittedly not one you can instantiate. Unit.toString() is a valid, if not generally useful, statement, for example.
  • Not all the versions that print out 10 are the same. Most importantly, f7,f8, and f9 are actually functions that return functions that return 10, rather than returning 10 directly. When you declare def f8 = () => {10}, you are declaring a function f8 that takes no arguments and returns a function that takes no arguments and returns a single integer. When you invoke println(f8) then f8 dilligently returns that function to you. When you call println(f8()) it returns the function, then immediately invokes it.
  • The functions f1,f3,f4, and f6 are all essentially equivalent in terms of what they do, they vary only in terms of style.

As "user unknown" indicates, the braces are only important for scoping purposes and do not make any difference in your use case here.

nonVirtualThunk
  • 2,842
  • 20
  • 20
  • 1
    You should change ‘Functions declared as def foo = 10’ to ‘Methods …’ for clarity. – Debilski Nov 28 '11 at 23:56
  • Regarding your last point, since `foo` is executed regardless of whether you put `()` after it, if i want the function `foo` itself, and not its post-execution return value, how do you do that? – Li Haoyi Nov 29 '11 at 00:25
  • 2
    The last point is incorrect. Scala allows you to omit empty parens when making a call. (You can even `def foo()()()()()()()() = 10` and call it with `val x = foo`.) @LiHaoyi - You are looking for `foo _`. In this case it will matter how many parens exist (zero or one: `()=>Int`; more produces a curried chain `()=>()=>...`.) – Rex Kerr Nov 29 '11 at 00:56
  • You can write `foo _`. That is the same as writing `() => foo`. btw. a method declared as `def foo() = 10` can also be invoked without parentheses. – Tesseract Nov 29 '11 at 01:04
  • 2
    "because in scala `Unit` is a valid object, though admittedly not one you can instantiate" You can: `()` is the only value of type `Unit`. – Alexey Romanov Nov 29 '11 at 08:47
  • @AlexeyRomanov quite so, I simply mean that you can't make a new instance of it, no `var a = new Unit` – nonVirtualThunk Nov 29 '11 at 16:07
  • @RexKerr your are absolutely correct, my apologies, I've grown too used to an IDE that enforces the convention I mentioned, I will correct it. – nonVirtualThunk Nov 29 '11 at 16:09
  • The 2nd bullet does not address the 2nd question. In `println(f5)`, the parens are supplied for you (empty application), so it's the same as `f5()`. It's not the case that `println(f5())` is invoking `Unit.apply()` or `()()`. – som-snytt Jan 26 '14 at 01:09
17
def f() {...}

is sytactic sugar for

def f(): Unit = {...}

So if you omit the "=" the method will always return an object of type Unit. In Scala, methods and expressions always return something.

def f() = 10
is sytactic sugar for
def f() = {
10
}

If you write def f() = () => 10, it's the same as writing

def f() = {
() => 10
}

So that means f is returning a function object. You could however write

val f = () => 10

When you call that with f() it returns 10 Function objects and methods can be used interchangingly in most cases, but there are a few syntactic differences. e.g. When you write

def f() = 10
println(f)

you get "10", but when you write

val f = () => 10
println(f)

you get

<function0>

On the other hand when you have this

val list = List(1,2,3)
def inc(x: Int) = x+1
val inc2 = (x: Int) => x+1
println(list.map(inc))
println(list.map(inc2))

Both println will print the same thing

List(2,3,4)

When you use the name of a method at a place where a function object is expected and the method signature matches the signature of the expected function object it is automatically converted. So list.map(inc) gets automatically converted by the scala compiler into

list.map(x => inc(x))
Tesseract
  • 8,049
  • 2
  • 20
  • 37
  • Following on from your last example, How does the work with zero-parameter-methods (like `f4`) which could be called even without the brackets? It seems to me there is ambiguity since `f4` could mean either the function itself or the result of evaluating the function, which could result in different things if the zero-parameter-method is no idempotent or has side effects, – Li Haoyi Nov 29 '11 at 00:17
  • It also works with zero parameter methods. I can't think of an example where there would be an ambiguity. However if the zero arg method is defined without brackets "def f = 10" then it won't work anymore for some reason. – Tesseract Nov 29 '11 at 00:54
  • And `def f(): Unit = {...}` in turn is syntactic sugar for `def f(): Unit = {{...; ()}}` – aij Oct 30 '15 at 15:23
15

Six years later, in a future version of Scala to be released even further in the future, things have improved:

That brings our 9 ways of defining a function and 15 ways of calling them down to 7 ways of defining a function and 10 ways of calling them:

object NewMain extends Thing{

    def f1 = 10
    def f3 = {10}
    def f4() = 10
    def f6() = {10}
    def f7 = () => 10
    def f8 = () => {10}
    def f9 = {() => {10}}

    def main(args: Array[String]){
        println(f1)     // 10
        println(f3)     // 10
        println(f4())   // 10
        println(f6())   // 10
        println(f7)     // <function0>
        println(f7())   // 10
        println(f8)     // <function0>
        println(f8())   // 10
        println(f9)     // <function0>
        println(f9())   // 10
    }
}

See also lampepfl/dotty2570 lampepfl/dotty#2571

As a result, it's relatively clear which syntax is optional (e.g. {}s) and which definitions are equivalent (e.g. def f4() = 10 and def f7 = () => 10). Hopefully, some day when Dotty/Scala-3.0 is released, newbies learning the language will no longer face the same confusion I did six years ago.

Li Haoyi
  • 15,330
  • 17
  • 80
  • 137
  • f4 and f7 are equivalent ? """ scala> f4 res15: Int = 10 scala> f7 res16: () => Int = $$Lambda$1059/402310578@75dc1d5a scala> f4() res17: Int = 10 scala> f7() res18: Int = 10 """ – Charlie 木匠 Apr 16 '18 at 19:18
4
def f1 = 10    
def f2 {10}    

The second form does not use an assignment. Therefore you can think of it as an Procedure. It is not meant to return something, and returns therefore Unit, even if the last statement could be used to return something specific (but it could be an if-statement, which would only have something specific in one branch).

def f1 = 10    
def f3 = {10}  

You don't need braces here. You need them, for instance, if you define a val, so the scope of this val is restricted to the enclosing block.

def sqrGtX (n:Int, x: Int) = {
  val sqr = n * n
  if (sqr > x) 
    sqr / 2 
  else x / 2 
}  

You need the curly braces to define val sqr here. If the val is declared in an inner branch, the curly braces don't need to be at the top-level of the method:

def foo (n:Int, x: Int) = 
  if (n > x) {
    val bar = x * x + n * n
    println (bar) 
    bar - 2  
  } else x - 2 

For further investigation when two methods return the same result, you can compile them and compare the bytecode. Two binary identical methods will be identic.

user unknown
  • 35,537
  • 11
  • 75
  • 121