Are default default parameters in Scala3 'using' clauses documented anywhere?

Scala 2 has had default parameters for a long time, but it seems that in Scala 3 one can use default parameters within a using (ie implicit parameter) block.

Are the semantics of this usage documented anywhere? I guess it means that if implicit search fails, it will use the default parameter instead?

Here’s an example usage of a Circe Json builder that builds Json Key/Value pairs if either a Circe Encoder[T], or failing that a Cats Show[T], is defined in implicit scope. It ~seems~ to work, but I wish I could find the docs to be more confident…

  extension (key: String)
    def ->>[T](t: T)(using enc: Encoder[T] = DummyEncoder[T], show: Show[T]): (String, Json) =
      if !(enc.isInstanceOf[DummyEncoder[T]]) then key->enc(t) else key->Json.fromString(t.show)

  class DummyEncoder[T] extends Encoder[T]:
    def apply(t: T) = ???

Scala 2 had this as well btw.

And, yes, IME it works as you described:

1 Like

The spec imported from Scala 2 still says:

If an implicit argument is not found by implicit search, it may be supplied using a default argument.

I see there was a ticket requesting clarification, and I see that I was going to add it in the section on “function applications” but was persuaded that it belonged in the section on “method definitions”, which is why I had trouble finding it just now.

Neither “applications” in chapter 6 nor “implicit parameters” in chapter 7 mention this interaction. This is odd because “named and default args” go together, but really, first you sort out “what the user wrote” as named and positional args, then you supply implicits, then you add defaults. (For overloading, this must determine “applicability”.)

This does not work:

scala> def f(i: Int)(using String = "hi") = summon[String] * i
-- [E040] Syntax Error: ------------------------------------------------------------------------------------------------
1 |def f(i: Int)(using String = "hi") = summon[String] * i
  |                           ^
  |                           ')' expected, but '=' found

scala> def f(i: Int)(using s: String = "hi") = summon[String] * i
def f(i: Int)(using s: String): String

I’m embarrassed that I used the same example from the spec, before I had looked it up. Am I in an example rut?

Chapter 7 says

A method with implicit parameters can be applied to arguments just like a normal method. In this case the implicit label has no effect. However, if such a method misses arguments for its implicit parameters, such arguments will be automatically provided.

But this works (and works the same in Scala 2):

scala> def f(i: Int)(using s: String = "hi", j: Int = 10) = summon[String] * (i*j)
def f(i: Int)(using s: String, j: Int): String

scala> f(2)(using j = 3)
val res3: String = hihihihihihi

I guess “has no effect” means “no further action is taken when an arg is supplied explicitly”, not that the parameter list is suddenly taken as “not implicit”. Some other comments on the PR also confused me on this score. In a word, I would expect chapter 6, after all, to explain how my expression f(2)(using j = 3) is interpreted.

Note that erroring for named applications was recently tweaked, so it’s important to ask these questions about how stuff works without assuming it must work as expected (for a given expectation of what is expected). Also, we must not assume that only “new” features need specification. (I still have to go back and see if a behavior with varargs changed, or if I never understood how it works, note to self.)

2 Likes