Given/Using are just a way to define default parameters

As I understand it,
given a:T …
is just a way to define a value of type T which will be used as default value when a T is expected.
def f(x:Int)(using y:T)
is very similar in intent to
def f(x:Int)(y:T=a)
which is to provide a default value if none is provided at call site.
And summon[T] designates this default value (a).

I propose to use the word default instead.
default T ... or default a:T ... replaces given a:T … to define a default value.
def f(x:Int)(default y:T) replaces def f(x:Int)(using y:T) telling that a default T should be used if no value is given for y at call site.
This could allow the following construct:
def f(x:Int)(default y:T=b)
meaning that if the compiler can’t find a default T at call site, it uses b.
And default[T] replaces summon[T] to reference this default value.

Less keywords, and a clarification of the intent, which is to provide a default value.

1 Like

For what it’s worth I agree with you this is cleaner. And “default” is a noun.

Because I missed the historical debate I am also unsure why we wouldn’t use “default” in place of both “given” and “using”. Ultimately, on one side (given) you are declaring a default, on the other (using) you are consuming a default. Both are very much connected and as a beginner I would prefer having them aligned with the same keyword.

This is the same kind of reasoning as declaring a case class with keyword “case”, and then pattern matching with same keyword “case”: semantic relation is made explicit and it is easier to relate to it.

given, using's to me are more useful for putting constraints to functions/classes. If you want default values, just use default parameters? Using another keyword default seems the same as going back to scala 2’s implicit?

7 Likes

" using 's to me are more useful for putting constraints to functions/classes"
Not really. It’s to create a new value, not a new type. But you’re right in the sense that we often do this by instantiating a (subclass of) an abstract class.

“If you want default values, just use default parameters?”
The purpose of given is the same as a default parameter: to have a default value when none is written at call site. The difference, and the strength, is that the compiler looks for this value in the call site environment.
Note that def f(i:Int)(t:T=default[T]) is not the same as def f(i:Int)(default t:T). In the first case, the value default[T] is searched in the environment of the definition of f.

“Using another keyword default seems the same as going back to scala 2’s implicit?”
implicit was far more general (conversions, extension methods), and it’s a good thing to distinguish these differents uses with different words.

I think that using the word default would make this concept quite simple and clear; it would not necessitate to understand why we summon[T]. default[T] is just the name of this default value of type T, created with default T ... and used by default with a parameter of the form default x:T (or default T when all that counts is the existence of such value).

Implicits are used for so much more than just default parameters. They’re used for typeclasses and providing evidence that one type equals another or that an object is valid for some operation. Not all givens take the form

given foo: Foo = ???

Naming it default would make it seem like that’s the only thing they’re supposed to be used for. I’ve never used them for default arguments, and I bet a ton of other people haven’t either. It doesn’t make sense to change the keyword.

9 Likes

For what it’s worth, the places in our codebase where someone has tried to use implicits as default parameters have generally become very hard to read, very quickly.

I wouldn’t go quite as far as to call this an anti-pattern, but I’ve never seen it used in ways that have improved the situation.

7 Likes

Not really. It’s to create a new value, not a new type

Well no but actually yes. With just an implicit conversion and given values we can apply constraints to types.

Here is an example of test a I did.

import io.github.iltotore.scalalint.constraint._

//Uses an implicit constraint behavior 
def printThisNumber(number: Constrained[Double, Positive]): Unit = println(number)

printThisNumber(14)
printThisNumber(0)
printThisNumber(-3) //Runime error

If inlined, the error is compiletime.

And as said above, implicits are much more than a default argument.

2 Likes