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