1. Overview
In this tutorial, we’re going to review the support of named and default arguments in Scala. We’ll show how these features can make our code better and illustrate them with some examples.
2. Default Arguments
In Scala, if a method has parameters, we have an option to define default values for them. It allows a method caller to write less code given the proper choice of default values:
def prettyPrint[A](input: Array[A],
start: String = "(",
separator: String = ",",
end: String = ")"): String = input.mkString(start, separator, end)
The method prettyPrint makes a nice String representation of a generic array. Its first argument, the input array, has no default values, whereas all the other parameters do.
Thus, we have two alternatives on how to call prettyPrint. First, if we are fine with the default parameters provided in the method definition, we can omit all the arguments except the first one:
prettyPrint(Array(1, 2, 3)) shouldBe "(1,2,3)"
At the same time, there is no flexibility loss. For instance, if we want elements of an input array to be wrapped in square brackets and be separated by semicolons, we can call prettyPrint with all the arguments specified:
prettyPrint(Array(1, 2, 3), "[", ";", "]") shouldBe "[1;2;3]"
3. Named Arguments
The previous prettyPrint example has an essential drawback. Since the majority of the method parameters are plain strings, the compiler could not prevent us from mixing up the separator with the start or the end, so we can end up with an ugly string representation instead of a pretty one.
Happily, the usage of named arguments mitigates this problem. Scala allows us to label method parameters with their names, which in many cases improves the readability of the code and makes it less error-prone. For instance, named arguments make the aforementioned prettyPrint call more transparent and self-explanatory:
prettyPrint(Array(1, 2, 3), start = "[", separator = ";", end = "]") shouldBe "[1;2;3]"
The only restriction we must remember using named arguments is that unnamed parameters must always precede the named ones. If we mess that up, the code won’t compile:
prettyPrint(start = "{", separator = ",", end = "}", Array(1, 2, 3))
4. Named and Default Parameters in Class Constructors
One of the most widespread examples of named and default arguments is applying them in class constructors. Since entities in many business domains often have many parameters with reasonable default values, it makes it easier to work with them.
Let’s illustrate it with a class representing a limited version of an order in some delivery service:
case class DeliveryOrder(product: String,
addressToDeliver: String,
amount: Int = 1,
promoCode: Option[String] = None,
byTime: Option[LocalDateTime] = None,
comments: Option[String] = None
)
DeliveryOrder has two mandatory and four optional fields, and it’s a simplification of a real-world example where we can have dozens of parameters. However, it’s quite evident how default and named arguments improve the code readability:
val orderWithNamedAndDefault = new DeliveryOrder(product = "Pho Bo",
addressToDeliver = "42 Some Street, suite 24",
promoCode = Some("SALE42")
)
val orderWithoutNamedAndDefault = new DeliveryOrder(
"Pho Bo",
"42 Some Street, suite 24",
1,
Some("SALE42"),
None,
None
)
orderWithNamedAndDefault shouldBe orderWithoutNamedAndDefault
Compared to orderWithoutNamedAndDefault, orderWithNamedAndDefault expression is less verbose, and it provides the reader with some information about the DeliveryOrder in the business model. As a result, it improves code readability.
5. Conclusion
In this tutorial, we learned how named and default arguments support in Scala can improve the readability of our code by making it less verbose.
As usual, the source code with all the examples used in this tutorial can be found over on GitHub.