Okay. Well, it definitely is! I think this decision has prompted a lot of the pushback; familiar constructs feel more welcoming than novel ones, typically.
I figured that this was the logic behind it, but it is kind of awkward because “implied” is both an adjective and a past-tense verb:
“He implied that his solution was better.” (Past tense verb.)
“The implied meaning is not always clear.” (Adjective.)
Because of this conflict, and no contextual cues in code to disambiguate, I find it mildly uncomfortable to read implied c for T
. Rather than comfortably settling on the adjective form implied c
as opposed to maybe explicit c
, it seems like a sentence fragment: who implied c for T, and when did they do it? I guess whoever wrote the code did? If it were present tense I could read it as imperative, a directive to the computer: imply c for T
. But it just ends up seeming vaguely awkward.
I expect quite some number of people will have the same discomfort with it that I do, and for the same reason (though it took me a while to figure out what the problem actually was and how to express it).
Can we then please limit it to the case where it works well? I don’t see any compelling use case for early given
s since implicit arguments are going away and thus the call chain becomes unambiguous. Just create a temporary class or newtype that wraps the given and proceed from there.
def foo given (i: Int) = GivenI(i)
case class GivenI(i: Int) {
def apply(c: Char) given (b: Boolean): Double = {
implied for Int = i
...
}
}
Yes, it’s a bit annoying to write, and maybe an optimizer will have to chew on it to get it zero-cost, and yes, formally you can now create GivenI(i) instances that act kind of like a partially applied function without actually being one.
But in exchange for removing a really awkward part of the syntax, I think it’s worth it.
(Note: more ambitiously, it could be an anonymous class: def foo given (i: Int) = new { def apply(c: Char) given (b: Boolean) = ... }
. Even more ambitiously, inline new
could be a directive to make it all resolve into a single method call.)
Also, on the calling side, it also makes everything clean:
(foo given 7)('e') given true
actually is much more like what it looks like (i.e. (foo given 7)
is an operation that produces an intermediate, GivenI(7)
, and that intermediate is applied. As it stands now, it is a very weird syntax for function application that is isomorphic to foo(7, 'e', true)
.
It is not very much better because of the past-tense verb form: “The import implied that we needed more functionality.” This brings to mind: “that we have an import implies (or did, when we wrote it) this module path”. Which is of course nonsense; we are using import
as verb (imperatively) to state that the things implied in foo.bar.baz._
should be accepted also. Neither verb past-tense-verb module-path-noun nor verb adjective module-path-noun make much sense.
It’s rather weird, even in the improved order.
Of course it doesn’t always have to make sense in English, but it is somewhat jarring, and it is best to minimize such things when possible.
Oh, right, just like Rust. For some reason I had decided that it was the instance of Conversion
that had to be parameterized, rather than the…uh…what do you call that thing? Synthesizer of implied instances? Anyway, oops–of course the type parameter should just be pushed out to the implied
.
No issues there, then!
(Except the how-do-we-control-precedence-of-compatible-inferences question, but that is a question throughout.)