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.

Wednesday, April 9, 2008

On defence of implicit arguments and the lack of type inference power

I found two quite recent comments by Martin Odersky (main developer of Scala) that give insight to two commonly arised questions. They are 1) why implicit arguments exists, and aren't they too dangerous? And 2) Why on earth is this so cool type inference system behaving like an idiot, and isn't inferring some obvious cases.

Here are the quotes and links to the original source.

1)

"In practice, of course, you want to find the right mixture between expressiveness and safety. -- -- and you'll accept some implicit parameters at places where they are really useful. And they are amazingly useful when used as a tool in the right hands.

For instance ScalaCheck lets you write properties of your programs and then constructs unit tests for these properties automatically. Several bugs in the standard Scala library have been found this way. Without implicit parameters, the plumbing you'd have to do to set up the properties would make them much uglier, so people most likely would not write any. So that's an example where implicits are really useful. Expect to see many more libraries that do amazing things like ScalaCheck in the future.

-- --

P.S. I think this discussion highlights a bit the design philosophies behind Java and Scala. They are not the same. Scala puts more trust in its users that they will do the right thing. Maybe this assumption is wrong, and users will make a mess of it, but that's what Scala is."

Martin Odersky, posted on March 9, 2008 at 6:16 am

Original post

2)

"The reason Scala does not have Hindley/Milner type inference is that it is very difficult to combine with features such as overloading (the ad-hoc variant, not type classes), record selection, and subtyping. I’m not saying impossible — there exist a number of extensions that incorporate these features; in fact I have been guitly of some of them myself. I’m just saying it’s very difficult to make this work well in practice, where one needs to have small type expressions, and good error messages. It’s not a shut case either — many researchers are working on pushing the boundaries here (look for instance at Remy’s MLF). But right now it is a tradeoff of better type inferencing vs better support for these features. You can make the tradeoff both ways. The fact that we wanted to integrate with Java tipped the scales in favor of subtyping and away from Hindley/Milner."

Martin Odersky, posted on April 9, 2008 at 2:08 am

Original post

I hope this is not violation of any copyright ;)

Another news regarding Scala are that the contractiviness requirement for implicits was removed by David MacIver's suggestion. Moreover in one month Scala is likely to have some kind of version of virtual classes, so it's possible to extend class hierarchies in one more powerful way. I hope there will be a good documentation available when it's out *cough* existential types *cough*. :)