trait Ctx
object Ctx:
val default: Ctx = new Ctx {}
def foo(arg: Int)(using ctx: Ctx = Ctx.default): Unit = {}
def foo(arg1: Int, arg2: Int)(using ctx: Ctx = Ctx.default): Unit = {} //error: two or more overloaded variants of method foo have default arguments
I want both foos to have a default values for their contexts, but I’m currently limited since I get the overloading ambiguity error.
Why isn’t overloading ambiguity checked on a block-by-block basis?
It’s not ambiguous. You can’t have default parameters for more than one of a set of overloaded methods. It’s one of the ways in which overloading isn’t quite first class. It annoys me greatly, because it makes it considerably harder to write lovely APIs.
But, anyway, it’s not an ambiguity issue. This also doesn’t work:
Incidentally, with named tuples there is a partial workaround, but you lose the ability to name your parameters in opposite order:
object Ugh:
type TwoArgs = (arg1: Int, arg2: Int)
type UghArg = Int | TwoArgs
inline def foo[A <: UghArg](a: A)(using bool: Boolean = false): Unit =
inline a match
case arg: Int => println(s"$arg $bool")
case args: TwoArgs => println(s"${args.arg1} ${args.arg2} $bool")
case _ => compiletime.error("Indeterminate argument type")
The limitation is there to prevent possible ambiguity at the call site. But clearly like in the case above, it’s not possible to create such ambiguity at the call site, so I’m wondering if there is a way to fine-tune the rules to allow for default parameters in methods that are clearly distinguishable by earlier blocks.
I don’t think the limitation was only to prevent ambiguity. It also meant that one didn’t have to worry about how to (stably) name the different defaults.
But, anyway, I would be happy if more non-ambiguous cases were allowed!
The limitation is entirely an implementation restriction. It has to do with how default parameters get compiled down to the JVM/JS/Native model. The type checker would be happy to deal with multiple overloads with defaults. It’s the rest of the compilation pipeline that cannot cope.