Question about context parameters: There's followi...
# language-evolution
d
Question about context parameters: There's following text in the KEEP: > For example, the following call to
foo
is declared ambiguous, since
"hello"
may work both as
String
or
Any
context parameter. >
Copy code
context(Any) fun foo() {}
> context(String) fun foo() {}
> 
> fun test() = with("hello") {
>     foo()
> }
> > The reasoning for this particular rule is that, since contexts are implicit, there is no way for the user to resolve to the other function if required. This implicitness also means that it's harder to figure out which overload is called from the program code -- once again, there's no value you can easily point to. In contrast, in the case of an explicit parameter, you can always use
f("hello" as Any)
to force using
(Any) -> R
over
(String) -> R
. Why the same reasoning about explicit casting can't be used here?
Copy code
fun test() = with("hello" as Any) {
    foo()
}
1
👍 1
💯 1
y
That's... a really good point actually.
a
that reasoning would apply (or should apply, the implementation is still coming). Let's consider the two scenarios: •
String
in context -> both
foo
apply, so we get an ambiguity error •
Any
in context -> only the one with
context(Any)
applies, so it should be the chosen one
y
@Alejandro Serrano.Mena but the reasoning says that for explicit parameters, it's okay that the algorithm accepts the more-specific overload because one can explicitly choose the less-specific one if they want. The OP post here is demonstrating that the same trick applies to contexts, and hence they're arguing that the
String
in context case shouldn't be ambiguous and should instead resolve to the
String
version of
foo
d
Exactly ^
p
I wonder if there's some syntax-level support desired for specifying context for a call explicitly. Something like
myReceiver.myMethod(context(a="hello" as Any), otherParam)
(terribel example syntax) scala has the implicit parameter list that can be explicitly specified if/as requried.
y
The new params keep introduces a
context
function that's basically a multi-with, so
context("hello" as Any) { myReceiver.myMethod(otherParam) }
is somewhat reasonable.
👍 1
a
ah, I understand what you mean now, that's for the clarification the reason why we thought that case should be different is because context parameters are not explicit in the call itself, but only explicit things should somehow drive resolution. So the current workflow is: 1. get the types of the value parameters 2. with that information resolve the call and available implicit parameters -> that's where you'd get an ambiguous reference error 3. resolve the context parameters for that chosen function
p
So can you clarify in the above case the compiler is expected to error:
Copy code
context(_: Any) fun foo() {}
context(_: String) fun foo() {}
fun test() = context("hello") { foo() }
but with the following it would be happy (and call the first
foo
):
Copy code
context(_: Any) fun foo() {}
context(_: String) fun foo() {}
fun test() = context("hello" as Any) { foo() }
in which case, there's no way to call the second
foo
- it would be entirely reasonable to resolve the function with more specific matching context types, no?
y
Also, there's no ambiguity error if using normal receivers:
Copy code
fun Any.foo() {}
fun String.foo() {}
fun test() = with("hello") { foo() }
1
a
@phldavies sorry, I made a mistake above, the types of available values in the context is take into consideration. So in the first case you have both
String
(and thus
Any
) in the context, so there's an ambiguity, whereas in the second case you only have
Any
, so there's no ambiguity @Youssef Shoaib [MOD] yeah, the language is not entirely uniform in that respect. But I think that adding more implicitness to the game is a recipe for developers to be confused whenever something doesn't resolve as it should. Note also the very different ways in which receivers and context parameters are resolved: • for the receiver we actually look inside the member scope of each implicit receiver, which means that
foo
"lives" inside of
String
, so it's chosen without ever going to the parent
Any
• for the context parameters the functions are actually top-level, both are found, and then we filter out by available context values
p
I'm concerned with that ambiguity of both
String
and
Any
being available is one the developer will be unable to overcome (as they aren't able to tell the compiler to treat the
String
in context explicitly as
String
and not as
Any
in order to remove the ambiguity. As such, with both
context(String) fun foo()
and
context(Any) fun foo()
in scope it is impossible to invoke
context(String) fun foo()
Playing with the now-defunct context-receivers: https://pl.kotl.in/vRfT2KACb It seems to work as expected regarding choosing the correct
context(BufferedWriter) log(String)
over
context(Writer) log(String)
which is just as applicable
a
I see the point, but as I mentioned, we tried to avoid as much as possible choosing overloads based on contextual values, which are very hidden from the eyes of the user Anyway, the idea is always to show a first version, and then re-evaluate whether some of these rules are needed. Especially things like this one (where an ambiguity error is no longer an error) are easier to "fix", as it turns non-compiling code into compiling one.