1

I am designing a program. I am new to Scala but it seems optional arguments are handled using the following:

def f1(my_string: Option[String] = None) = {

// Maybe do some pattern matching here to extract the value

}

However, for an end user this is pretty ugly as they will need to call this function like:

f1(Some("foo")

Is there a technique or pattern to turn this into:

f1("foo"))

and still work with an optional argument?i.e f1() also works?

The reason I ask is I have obviously used Scala libraries where adding an explicit Some(..) has not been necessary, but in their source code they have defined functions as above. I would personally use a default argument but wandering why this is a design pattern.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
finite_diffidence
  • 893
  • 2
  • 11
  • 20

3 Answers3

4

An option is to create a helper class only for such arguments to avoid the anyToOption implicit conversion in the linked thread:

class OptArg[A](val asOption: Option[A])

object OptArg {
  def NoArg[A] = new OptArg[A](None)
  implicit def fromValue[A](x: A): OptArg[A] = new OptArg(Some(x))
  // optional
  implicit def toOption[A](arg: OptArg[A]) = arg.asOption
}

// use
def f1(my_string: OptArg[String] = NoArg) = {
  // can use my_string as if it was Option,
  // or call my_string.asOption explicitly
}

In simple cases of course overloading will be more suitable, but not if you have many optional arguments.

The drawback is relying on implicit conversions, but I think this one is pretty benign and unlikely to trigger accidentally.

OTOH I think it's quite rare in practice that an argument is optional but "no argument" doesn't correspond to some default value of that type, so I'd look if it can be defined as

def f1(my_string: String = something)

to avoid the whole problem.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
2

Instead of Option you could overload the method.

f1() :ReturnType = {...}

f1(arg:String) :ReturnType = {...}
jwvh
  • 50,871
  • 7
  • 38
  • 64
  • Thanks @jwvh, just as a matter of interest, if I was working with a class instead, would the idiomatic way still be overloading the constructor? – finite_diffidence Jun 12 '20 at 19:35
  • 1
    In that case you probably want to use an **auxiliary constructor**. They have [some restrictions](https://stackoverflow.com/a/14210930/4993128) but can be useful. – jwvh Jun 13 '20 at 00:58
2

Syntactic cost of Option in calls such as

f1(Some("foo"))

is trivial as compared to the value of minimised risk of null pointer exceptions in a large codebase. Hoare, a Turing Awardee, appologised

I call it my billion-dollar mistake. It was the invention of the null reference in 1965.

Mario Galic
  • 47,285
  • 6
  • 56
  • 98