Saturday, September 12, 2009

New to Scala? Study the Syntax. CAREFULLY.

So, it only took me about five seconds to go from having a Hello World Scala program working to having a 10% more complex Hello World program failing in a way that I had no idea how to fix.

Here's my program:

object HelloWorld {
def main(args: Array[String]) {
println(testfunc)
}

def testfunc() {
"Hello World!"
}
}

and the output of this awesome program? Just this...
()


If you're a seasoned Scala programmer, you can probably see already why I'm such an idiot, but there's an important point to be made here.

I was trying to test out two things with this example. First, this being the first time I've done functional programming in almost ten years, I wanted to "practice" the idea that the last expression in a function is the return value. Yes, I wanted to practice not writing "return". Secondly, I had read that Scala let's you invoke functions without parentheses, so I thought I'd give that a go by calling "testfunc" instead of "testfunc()".

But neither of these were the problem here. The problem was that, during all my Java-ingrained, time-poor reading of Scala tutorials, I'd had seen but never cared to take particular notice of one small Scala detail: function declarations and function definitions are separated by an equal sign:


object HelloWorld {
def main(args: Array[String]) {
println(testfunc)
}

def testfunc() = {
"Hello World!"
}
}

This code produces the output I was expecting at first: that magic phrase "Hello World!"

I'm actually really glad this happened. It wasted ten minutes of my time, but it taught me a real lesson that I've forgotten through not learning new languages often enough: sit at the computer and type the examples in. No amount of reading examples will actually make you understand a language or teach you how to write it. Only by sitting down, typing in every symbol from an example and understanding what it does (and figuring out where you stuffed it up) can you hope to become proficient.

There is a corollary lesson here, too: no matter how much people tell you Scala is like Java and how much you want to believe it, Scala is NOT Java. There are little differences which, as in this example, can make a HUGE difference. My approach from here on in will be to try and forget about "migrating" my knowledge from Java (the language) to Scala, and just approach Scala as an absolutely new language that needs learning from scratch.

Lastly: The thing that got me really confused is that my original code, complete with the nonsensical error, compiled and ran! To be honest, I still have absolutely no idea what that code is actually doing. If I change my 'println(testfunc)' to 'println(testfunc.getClass)', the output is:
class scala.Predef$$anon$1
So, I'm guessing that my function definition without the equal sign is somehow defining an anonymous type rather than a function, but I really am guessing - I actually have no idea. I'm looking forward to burying my head more and more into Scala syntax until one day I finally gain the knowledge to understand my mistake and jump up screaming "Aha!"

UPDATE:
Well, it only took a little more reading to discover what the original code is doing. Despite Scala being a primarily functional language, it still has the concept of "procedures", or what would be void methods in Java. These are methods that don't return a value, and they are defined using the exact same syntax as a function, except without the equal sign.

So, my original assumption - that my function was not being invoked but was instead being passed to println as some kind of anonymous type - was quite wrong. The function IS being invoked, but it doesn't return anything. But why does the code compile and run if the method returns nothing? That's because everything in Scala has a value. Even procedures that don't return anything still have a value! Crazy. (But fun.)

4 comments:

  1. Okay, your UPDATE clarifies why main() seems to work without using '='.

    ReplyDelete
  2. Many of us have suggested that the "def foo {" syntax (without the =) should be deprecated and removed for exactly this reason. Its intent was supposedly to make the transition from Java less challenging, but of course the added complexity makes for the exact opposite. The only argument in its favor is that once you're used to it, it makes it obvious what methods return Unit without requiring explicit annotation; since the return type is often inferred in idiomatic code, if all methods used the "=" syntax, it might be hard to see which were actually intended to return Unit. Personally, I never use this syntax and always explicitly annotate a return type of Unit and use the = syntax.

    ReplyDelete
  3. In scala, there aren't any methods that return nothing. The value that came out is of type Unit, and the only instance of Unit is (). You just happened to find a syntax shortcut for producing Unit.

    These are both the same:
    def test(){}
    def test() = ()


    I really had a hard time understanding why this would make sense to anyone when I first started using scala, but when I started looking at the types of functions, and remembering a few pain points from using function references in C#, it all started to make sense.

    In c#, there are 2 ways to store a reference to a function, depending on whether or not the return type is void. There are 'Action' delegates, and there are 'Func' delegates.

    All of the 'Action' delegates have type parameters for the number of function parameters, and all of the 'Func' delegates only have type parameters for the number of function parameters, plus an additional type for the return type.

    So because 'void' isn't an actual type with an actual value in C#, there is no way to refer to the return type of a function that returns void.

    Scala avoids that completely, all functions return something, and there is only one set of interfaces for dealing with functions instead of 2. And to make things even better, someone can ask for a function that takes an int, and returns anything. For a horribly contrived example:

    scala> def times2(i:Int) = i*2
    times2: (i: Int)Int

    scala> def intFunctionTest(func: Int => Any) = func(10)
    intFunctionTest: (func: (Int) => Any)Any

    scala> intFunctionTest(times2)
    res1: Any = 20

    scala> intFunctionTest(println)
    10
    res2: Any = ()


    When I passed times2, it multiplied 10 by 2, then returned the value, which was then returned from intFunctionTest.

    When I passed println, it printed 10, then returned Unit.

    I could even have written my function like this:
    def intFunctionTest[T](func: Int => T):T = func(10)

    and when I passed a function that returned an int, the result would have been an int instead of 'Any'

    ReplyDelete
  4. Nothing in Scala returns nothing, but a function can be declared to return Nothing! No contradiction here: such a function never returns. It either loops forever or throws an exception.

    [Nothing is the type with no instances. ]

    ReplyDelete