So, I just tried using the implicits as currently envisioned (given
/ using
) to solve some actual coding problems, with attention to how they work.
In so doing–while editing the compiler to change the syntax to try out different things–I found that it doesn’t actually make a whole lot of difference to me what the given
keyword is. It could be given
, could be witness
, could be provide
, could be the
, even. With a little practice you can get used to it. I personally like provide
best out of the things I’ve tried. I think it’s a significant but modest improvement.
Aside
While editing the compiler, I realized that it’s really nice that GIVEN
, Given
, etc. can be a noun inside the compiler code. This is because flags and tokens and such are all items (i.e. nouns), so it’s nice if the identifier is a noun! However, I don’t think this should affect whether the language uses verbs or nouns or whatever in various places.
I did notice that nouns made the compiler code nicer to read, though!
End Aside
The bigger problem, though, is the order of types and values. This comes because both of these things are supposed to be valid:
given T = t
given t as T { def foo = "salmon" }
This is weird and hard to adjust to, at least for me. In the first case, you’re associating a preexisting type with a previously named term. In the second case, though, you’re declaring a new term by specifying a preexisting type and then creating an anonymous implementation of it. You have the same kind of problem you had with given/given
: two considerably different constructs sharing the same terminology, which impedes mental switching.
I don’t yet have a good solution, but I think this is worth more thought. It pretty much bakes in an inherent conflict into the language. Changing as
to for
or is
doesn’t really help; it’s the type, instance vs. newname, type { anonymous extension } ordering which is a pain.
So I really hope these things can be unified somehow.
One idea, which I don’t really like but maybe can be salvaged into something usable, is to introduce new terms or use pre-existing ones without a syntactic cue save for the braces after the type name.
given t as T
means that we can use an already named t
when we need a T
, while
given t as T { ... }
means that we define a new t
for the cases where we need a T
.
I don’t like this because it makes the introduction of new terms hard to distinguish from the usage of old terms. However, it does help a lot in making givens take on a predictable structure.
Alternatively, we could insist on the familiar old :
notation to introduce new terms and either give up the abbreviated syntax entirely, or use an abbreviated new
form:
given T = new { def foo = "salmon" }
given t: T = new { def foo = "perch" }
This has the downside of being irregular ( new { ... }
means new Object { ... }
normally), but it has the upside of preserving the usual expected syntax for declaring things. Now we always have
given [optional new term name :] Type = theAppropriateTerm
This is my favorite so far (haven’t played with it, since the compiler changes needed to get this to compile are beyond what I can do in a matter of minutes), but I still am not totally happy with it.
(Note: parameters can be handled fine, but it just gets more irregular:
given foo(i: Int)(using A): T =
new(i, summon[A]) { def thingy = "halibut" }
)
Anyway, after using it, I’m now more worried about this than whether it’s called given
or provide
or paragon
or archetypical
or whatever. I really hope there’s a chance to fix this. I think it’s a substantial wart as it is. (I think provide t: T = new { ... }
also works better than given
.)