views:

137

answers:

3

Does anyone know if something like this is possible in Scala:

case class Thing(property:String)

def f(thing:Thing, prop:String = thing.property) = println(prop)

The above code doesn't compile; giving the error error: not found: value thing at thing.property

The following shows the expected behaviour:

f(Thing("abc"), "123") // prints "123"
f(Thing("abc"))        // prints "abc"

I realise I could make the prop argument an Option[String] and do the check in the function definition, but I was wondering if there was a way around it with the new named/default argument support in 2.8.0.

+1  A: 

This is exactly what Option is for. You can use the getOrElse method before the method call or inside the method f.

scala> val abc = Some("abc")
abc: Some[java.lang.String] = Some(abc)

scala> val none: Option[String] = None
none: Option[String] = None

scala> println(abc getOrElse "123")
abc

scala> println(none getOrElse "123")
123

scala> def f(o: Option[String]) = println(o getOrElse "123")
f: (o: Option[String])Unit

scala> f(abc)
abc

scala> f(none)
123

Oh here is something you could do via default arguments:

scala> case class Thing(property: String = "123")
defined class Thing

scala> def f(t: Thing) = println(t.property)
f: (t: Thing)Unit

scala> f(Thing("abc"))
abc

scala> f(Thing())
123

The expected behaviour can be achieved with simple overloading. I needed to put the method in an object because it looks like the REPL does not allow direct overloaded function declarations:

scala> object O {
         def overloaded(t:Thing) = println(t.property)
         def overloaded(t:Thing,s:String) = println(s)
       }
defined module O

scala> O.overloaded(Thing("abc"), "123")
123

scala> O.overloaded(Thing("abc"))
abc
michael.kebe
As mentioned, I realise I could use `Option`, but in the actual use case, the argument is already an `Option`, so I would end up with `Option[Option[String]]`. While this is perfectly valid, I wanted to avoid it if possible. To complete your first suggestion; the implementation would rather be `def f(t:Thing, o:Option[String] = None) = println(o getOrElse t.property)``f(Thing("abc"),Some("123))`The second suggestion isn't what I'm after - the default must be specified at the function declaration, as different functions have different default values for the property.
Kristian Domagala
+11  A: 

Yes, it's possible in Scala 2.8. Here's a quote from the "Named and Default Arguments in Scala 2.8" design document:

Since the scope of a parameter extends over all subsequent parameter lists (and the method body), default expressions can depend on parameters of preceding parameter lists (but not on other parameters in the same parameter list). Note that when using a default value which depends on earlier parameters, the actual arguments are used, not the default arguments.

def f(a: Int = 0)(b: Int = a + 1) = b // OK

And another example:

def f[T](a: Int = 1)(b: T = a + 1)(c: T = b)
// generates:
// def f$default$1[T]: Int = 1
// def f$default$2[T](a: Int): Int = a + 1
// def f$default$3[T](a: Int)(b: T): T = b

According to this, your code may look as follows:

scala> case class Thing(property:String)
defined class Thing

scala> def f(thing:Thing)(prop:String = thing.property) = println(prop)
f: (thing: Thing)(prop: String)Unit

scala> f(Thing("abc"))("123")
123

scala> f(Thing("abc"))()
abc
Vasil Remeniuk
Thanks, that's exactly the kind of thing I was after.
Kristian Domagala
You're welcome :)
Vasil Remeniuk
+2  A: 

Another simple solution is just to overload the method:

case class Thing (property: String)

def f(thing: Thing, prop: String) = println(prop)

def f(thing: Thing) = f(thing, thing.property)
Jesper
If I'm not mistaken, this is how C++ defines optional / defaulted parameters, right?
Randall Schulz
It's a long time I programmed in C++, but you can ofcourse do the same thing in C++ or also in Java.
Jesper