Sunday, April 13, 2008

Named arguments

edit at 3rd of March, 2010: Scala will have named arguments in 2.8. Great! http://www.scala-lang.org/sid/1#

Scala unfortunately doesn't have named arguments. I would really like to have them, because they make code much clearer in some cases, and it's harder to make mistakes with them. That's why I'll show one way to simulate them.

First, let's create the function that can handle homemade named arguments:

    def f(x: {val name: String; val age: Int}) = {


f takes one argument, that contains the actual parameters we want. The type is shorthand for Any{ ... }.

Next we have imported the contents of x to be visible inside method f. This is possible due to Scala's handling of objects as first class modules.

    import x._


And now we can use the attributes as if they were f's real parameters:

    println(name + " is " + age + " years old")


How about from client's perspective? Well, client can just create a structural type that contains the needed attributes (actual arguments).

    f(new {val name = "Anthony"; val age = 5})


As you see, it's not really possible to make mistakes this way. But the calling has too much boilerplate, namely vals. So let's make it a bit better:

We create a case class for each actual parameter.

    case class Name(name : String)
case class Age (age : Int)


Now we can make very clear function signature:

    def g(x: Name, y: Age) = {


Unfortunately we have to import all parameters one by one.

    import x._, y._
println(name + " is " + age + " years old")


The client can call the method in the following way:

    g(Name("Tim"), Age(2))


That's it. It's annoying that we have to create case classes, but it's tolerable in important cases.

Here's the full code:


object Test extends Application{
def f(x: {val name: String; val age: Int}) = {
import x._
println(name + " is " + age + " years old")
}
f(new {val name = "Anthony"; val age = 5})

case class Name(name : String)
case class Age (age : Int)

def g(x: Name, y: Age) = {
import x._, y._
println(name + " is " + age + " years old")
}
g(Name("Tim"), Age(2))
}

Run the code and you get as output:
Anthony is 5 years old
Tim is 2 years old

edit:

On the client side, often one passes some constant values. If you call just like f(value1, value2, ... valueN), it's hard to know later when you see the code what the meaning for each value was; so you have to go see the API (if there's no tool help). Instead you have to define vals before the call:

val age = 8
val name = "Jim"
f(age, name)


But now there's the issue that you have to write the variables twice, and it would be nice to see immeaditely on the call what the value was.

Then:

f(new {val age = 8; val name = "Jim"})
isn't that bad at all. Neither the case class equivalent. If you have many arguments to pass, calling with single argument per line looks nice:
f(new { val age = 8
val name = "Jim"
val hobby = "blogging" })


Named arguments are especially useful when constructing objects, because it's then when you have to pass lots of vaguely related arguments.

5 comments:

Anonymous said...

Sounds like a lot of extra work for a feature which is dubious at best. :-) Don't get me wrong, I like named parameters as much as the next guy, but I think that a well-designed API has no need for them. If your method signatures are actually so long that named parameters clarify anything, then you should seriously consider refactoring a bit.

Bas said...

Another difference between the two: in the f() case the caller can use any order for the arguments, while in the g() case the arguments have to be in the order specified in the signature.

Henrik Huttunen said...

Hi Daniel. The most useful example I can give is when types are the same. def f(width: Int, height: Int)

It's so easy to call f in wrong order, and introduce hard to find bug, and I think it isn't too bad to use aforementioned way.

bas, yes, I forgot to mention that. Thanks!

Stephan.Schmidt said...

1.) Nice to see the same people commenting to Scala posts ;-)

2.) Nice, I've written about named parameters in Java some time ago

http://stephan.reposita.org/archives/2008/05/02/never-never-never-use-string-in-java-or-at-least-less-often/

Not that nice - as it is Java - but usable. Taking your post I'll try your ideas with my Scala proceedings.

Thanks
Stephan

Nobody said...

Named arguments are coming to 2.8.0... :)