A common pattern in many languages is:
foo{
...block...
}
foo(bar = blah1, qux = blah2){
...block...
}
This is commonly seen in XML:
<foo bar="blah1" qux="blah2">
...block...
</foo>
But is also common in modern Kotlin where the last parameter of a function is treated as a block:
fun doSomething(name: String = "default", action: () -> Unit) {
println("Hello, $name")
action()
}
doSomething("Kotlin") {
println("Inside lambda")
}
doSomething{
println("Inside lambda")
}
Or Swift:
func doSomething(name: String = "World", action: () -> Void) {
print("Hello, \(name)")
action()
}
doSomething {
print("No name passed") // uses default "World"
}
doSomething(name: "Alice") {
print("Name passed explicitly")
}
Scala does not have support for this. It is possible to hack around to kinda-sorta make it work, as we have done in the Mill build tool:
def Task[T](t: => T): Task[T]
final class NamedParameterOnlyDummy private[mill] ()
def Task[T](
t: NamedParameterOnlyDummy = new NamedParameterOnlyDummy,
persistent: Boolean): ApplyFactory = new ApplyFactory(persistent)
class ApplyFactory private[mill] (val persistent: Boolean) {
def apply[T](t: => T): Task[T]
}
def foo = Task{ ... } // hits the first overload
def foo = Task(persistent = true){ ... } // hits the second overload
However, this approach has downsides. Apart from the t: NamedParameterOnlyDummy sentinel value, the fact that this involves overloading also means that target-typing and a bunch of other type inference features donāt work. In contrast, the approach taken by Swift and Kotlin do not involve overloading at all, and simply let you pass the last parameter in any parameter list as a separate curly-brace block.
Of course, it is always possible to spell this syntax:
def foo = Task{ ... } // hits the first overload
def foo = Task(
persistent = true,
block = { ... }
)
Or
def foo = Task{ ... } // hits the first overload
def foo = Task{
...
}(given persistent = true)
But this is verbose an unintuitive. In general, the concept of āblock of code, with some optional config in the headerā seems pretty universal across languages. So although Scalaās equivalent syntaxes are semantically equivalent, it is syntactically very awkward and doesnāt feel familiar to use.
Is there anything we can do to simplify this use case in Scala?