Currently, Context Bounds cannot be referenced inside the default values of parameters in the same method:
// No given instance of type Monad[F] was found for parameter
def foo[F[_] : Monad](something : F[Int] = summon[Monad[F]].pure(1)) : F[Int] = something
// Not found: M
def bar[F[_] : Monad as M](something : F[Int] = M.pure(1)) : F[Int] = something
I see two solutions:
expand def f[T : B](...) to def f[T](using B)(...) and def f[T : B as b](...) to def f[T](using b: B)(...) correspondingly.
add the same bounds to generated methods for default params. In case of explicit using clause it should be added to generated methods calls.
Motivation
It is impossible to use context bonds in highly generic methods without ability to reference them.
Proposed change will make usage of context bounds more intuitive and convenient.
What if bounds for generated methods would be added only if the bound has been used? Then old sources, as I think, will remain the same. And only usage of bounds in default params will cause no backward compatibility
The other issue with putting the using parameter at the beginning is that it causes issues with type inference:
trait Bar[T]
def foo1[T](using Bar[T])(x: T) = x
def foo2[T](x: T)(using Bar[T]) = x
given Bar[Int] = ???
given Bar[String] = ???
//foo1(0) // error: Ambiguous given instances
foo2(0) // works
I think that would work (modulo above), but it would also mean adding a default value to a parameter can sometimes cause breaking backwards compat
(because it would move the using parameter)
IIRC @smarter proposed allowing the variable to be used before it is declared in this very specific case
This would mean you can do things with [T : A] that you cannot with (using T: A[T]), no matter where it is placed
which might cause trouble down the line ?
Note:
We cannot just change the rule to “default values can refer to any parameter”, because this would lead to loops:
trait Box:
type Value
// type of y depends of value of x, but value of x depends on y
def foo(x: Box = y)(y: x.Value)
However, this could never happen with [T : A](params), since A cannot depend on params