-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Trial: given as
instead of delegate for
#6773
Conversation
I'm tentatively enthusiastic. |
Direct link to docs: There is also a change to show a use case for a regular export. |
b921b7b
to
fdb718f
Compare
I agree with Jon: still mulling it over, but I like |
I'll voice my support for
|
In order of preference:
Although honestly I will be happy with any solution which is not |
my 2 cents:
these 2 guidelines are IMHO the most important thing, the particular keywords are not that important
👍 on not using |
This was considered but discarded since |
I really like A new proposal: class ListOrder[A](implicit A: Order[A]) extends Order[List[A]] { } I propose to change the class ListOrder[A] proves Order[List[A]] given Order[A] { } or class ListOrder[A] gives Order[List[A]] given Order[A] { } This is syntactically familiar with OO programmers. If |
As an early skeptic of changing I think that |
Another bonus to given Ord[Int] as {
...
} |
I'm happy with using |
I agree But I think I'd prefer if anonymous instances didn't have the trailing given as Ord[Int] { ... } have given Ord[Int] { ... } which I read as "given an Ord of Ints, defined as ....". Secondly, I'm particularly unsure about
val as: List[A] = ... |
DelegateBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody] | ||
| ‘given’ GivenDef | ||
GivenDef ::= [id] [DefTypeParamClause] GivenBody | ||
GivenBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody] | ||
| ‘for’ Type {GivenParamClause} ‘=’ Expr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still uses for
I am open to use given A as B
given C[X] as D as two given definitions and given A as B
given C[X] as a single definition with a given clause.
Not sure which one works better. I felt |
Sadly neither |
I think given Ord[Int] as IntOrd { ... }
given Ord[Int] as { ... } and given IntOrd for Ord[Int] { ... }
given for Ord[Int] { ... } though the former reads more nicely when defining an anonymous instance. |
You have to twist it around then. The thing that's actually given follows. |
@jeremyrsmith The way I see it is that the first is the actor (the implementation), the second the role (the type). So it's "Given Daniel Craig as James Bond, ... ", not "Given James Bond as Daniel Craig". |
Any chance we can use given IntOrd: Ord[Int] as { ... }
given Ord[Int] as { ... } and for that disambiguation case you gave:
It would become given B: A as { ... }
given D: C[X] as { ... }
// two given definitions
given B: A
given C[X] as { ... }
// one definition with a given clause WDYT? |
Not sure about that analogy 😀 since Daniel Craig (presumably) plays many roles, but an instance will only play one role – so the role is the thing that's important (and indeed I think most people will not even bother naming instances most of the time now that anonymous instances are allowed).
But anyway, I don't think it matters that much. |
@dwijnand At the risk of appearing heretical, I'd propose to use given IntOrd: Ord[Int] = new { ... }
// Anonymous:
given: Ord[Int] = new { ... }
// Conditional:
given OptionOrd[A] given Ord[A]: Ord[Option[A]] = new { ... } This also unifies the syntax with given global: ExecutionContext = myForkJoinPool I can see the criticism coming, but don't think this is really "exposing the mechanism rather than the intent." Behind the scenes, the instance may still be cached or not, like with the current proposal. On the other hand, I think the fact we are creating a new instance of a type is really better made apparent, using the syntax that's familiar for these concepts. I'd wager that no beginner will ever be comfortable and productive using type classes before they understand these basic things, so it's best not to hide it from them. |
The syntax with |
@sjrd I don't think the public members would be visible from the outside, because the type of the instance is annotated (and the type annotation would be required by the compiler). In this respect, trait T
given A: T = new { val x = 0 }
A.x // error would behave the same in that respect as: trait T
def A: T = new { val x = 0 }
A.x // error |
We want to get away from |
On further reflection, what bothers me a little here is that the arrows are flipped: the first In teaching Scala 2.x, I have had to explain that the implicit in They are two different meanings hiding behind the same keyword.
provision ListOrd[T] as Ord[List[T]] given Ord[T] { ... } As an aside, I do like provision ListOrd[T]: Ord[List[T]] given Ord[T] { ... }
provision _ : Ord[List[T]] given Ord[T] { ... }
// ???
// provision: Ord[List[T]] given Ord[T] { ... } for some suitably better |
@jdegoes
As such, the left-most It makes sense to me that way. |
In teaching, I would not present that model, as it's both unnecessary and confusing. I would teach only that
This is the thing that bugs me: the "left-most given" means "provide". I don't really care all that much, but generally I find teaching is easier when concepts with separate semantics have separate names. |
as I've said elsewhere, I'm glad we are still exploring alternatives to the repetition of I find |
I suggested to use the word |
I think I still like
|
Then how do you explain that a I have to agree though that the repetition in |
You don't need to explain it that way. You say: If your function def sort[A](list: List[A]) given Compare[A]: List[A]` Now You may only call For example, you can only call Good teaching benefits from many techniques, but the 2 most relevant here are:
|
Allow newline characters before a `(` token that starts a formal parameter list.
bb073b4
to
e9e926c
Compare
I squashed all relevant commits into one so that we can easily revert. Anybody wants to review the technical details or should we merge as is? |
I've just given it a once-over ... LGTM. On a line-by-line basis at least, given/as reads a lot better than delegate/for both in the code and the docs. |
OK, merging. To be sure, it's trial only at this stage. |
On Tue, Jul 2, 2019, 11:43 AM Sébastien Doeraene ***@***.***> wrote:
@jdegoes <https://github.com/jdegoes> given *parameters* already mean both
- *require* the thing to be available *at call site*, and
- *provide* the thing *at definition site.*
Wasn't this one of the biggies in the list of problems with implicits?
I really would like to see a full side-by-side of all the problems with
implicits, and how the new thingy stacks up. And for anything that isn't
addressed, a convincing explanation of why addressing it isn't important
and won't need to be addressed in the end anyway, or why it's possible to
address it later.
If we aren't keeping the problems clearly in mind then our solutions may
not fit.
|
I don't think there's full consensus about which things are actual problems and which are not. |
On Tue, Jul 2, 2019, 11:39 AM John A. De Goes ***@***.***> wrote:
In teaching Scala 2.x, I have had to explain that the implicit in implicit
val foo means that the value foo will be made available to (*provided to*)
implicit search resolution; and that the implicit in def foo(implicit
bar: Bar) means that the value bar will be required from (*given from*)
implicit search resolution. The arrows are going opposite directions.
They are two different meanings hiding behind the same keyword.
In this case I don't see the issue. Implicit is an adjective. There are no
arrows. Implicit always is just an attribute. It isn't saying "make this
part of implicits search"; that's backwards, implicit search means
searching for things marked implicit. Similarly, when you call a method, an
implicit parameter list, as a courtesy the compiler permits you to omit the
parameter list with that attribute.
Ironically, it sounds a tiny bit like the difference between imperative,
where code is about saying what should happen, and functional, where you
are mainly expressing data, which just is, but which then has operations
that depend on it...
Anyway I'm not sure how you solve this in the case where you're defining a
method that takes implicit parameters and wants them to be implicit within
its definition, without requiring you to write two keywords (or symbols).
And if you're okay with that, then it's really easy to solve. The real
problem is that that is such a ubiquitous case.
|
On Fri, Jul 5, 2019, 4:05 AM Jasper Moeys ***@***.***> wrote:
If we aren't keeping the problems clearly in mind then our solutions may
not fit.
I don't think there's full consensus about which things are actual
problems and which are not.
The question isn't whether there's consensus. That's a separate problem.
The question is what premises the team is working with, as far as what
problems are problems and how they are being solved, and what problems are
not problems and why not, and if they know what their premises are then
they should share them.
—
… You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#6773>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAAYAUHY7INW5Z666YYOHGTP536GLANCNFSM4H4QZRHA>
.
|
@odersky shapeless-3 migration from implicits to givens here: milessabin/shapeless@cd0ac77. By and large I would say this is a change for the better. |
I don't think it's a winner. given would then be used in two different ways with two different underlying semantic meanings. given as I believe refers to a known or established fact "at a couture house, attentive service is a given" or "IntOrd is a given" (noun). The other is more like taking into account e.g. "given the complexity of the task, they did a good job" or "given an Ord[T] I will provide a List[Ord[T]]. The second one is very neat the first quite clunky, and the difference between the two hard to explain. |
I agree with @jdegoes, having "two different meanings hiding behind the same keyword" is confusing, in particular for learners. I like "given" for requiring, but not for defining. "delegate" was fine for me (simple change: just revert the commit), other suggestions like "instance" or "impl" are also fine – let's just use a different word. |
IMHO, delegate was a better option. Can I know what are the objections to |
Has |
FWIW (I’m hardly using implicits): The sort example becomes something like: |
I don't think Especially when lines get long this seems really unfortunate.
At least with I don't have a positive suggestion for the declaration site beyond agreeing that delegate isn't great. I think (For similar reasons, not that it's being proposed, I also oppose adding |
@jdegoes wrote:
Could someone point me to where it was discussed ? |
@rjolly It was discussed briefly in the Scala Contributors forum; was rejected since |
I'm more inclined towards |
Also
|
I like In terms of meaning it is spot-on. It's the exact word you'd use to describe what this does: it marks a value as the canonical instance of a type.
|
But is it canonical? Why can there then be ambiguity when you are looking for that canonical value? It is by definition not canonical if there can be multiple definitions of it. Canonical is also an uncommon word, at least for non-native speakers, so it could be confusing compared to simpler words like give or provide. |
Since we are about to try out different syntaxes, here's another trial: Use
given as
instead ofdelegate for
.Why?
Reactions after the introduction of
delegate
were mixed. Most people acknowledged that it was better thanimplied
, but stated that they were not completely comfortable with the new syntax either. It's true that so far every alternative proposal was mostly disliked (until it was withdrawn, that is, that's when votes in favor tended to come out). At the same time there were suggestions to usegiven
as a noun (from Miles, Adriaan, maybe others) that felt to me like it could work. So I do not intend to re-open a general brainstorming for good terms. We got nowhere the last time we tried that. But it's worthwhile to try this single idea and compare it todelegate
(which as far as I am concerned would work as well).What's in the PR?
An alternative syntax
in place of
Both syntaxes are allowed (the docs in the PR changed to describe the new one). The idea is to keep just one alternative after about one release cycle and drop the other. During that release cycle it would be good to give the syntax maximal trial exposure.
Alternatives
There is one potential issue with the new syntax, in that
given
is now used as the name of implicit instances as well as implicit parameters. In that sense, it's similar to what implicit was before. This might look confusing, as in this example:We might want to think about different keywords for the parameter. Miles proposed
where
, but thatdoes not work so well if what we pass is a context instead of a typeclass instance. E.g. in
dotc
itself,reads not as nicely as
Another possibility is to go back to
with
, which is generic enough to mean anything, but is also annoyingly unspecific.Or we might conclude that there is no problem with
given
, after all, which is my current default assumption. In any case conditional givens of the form above are not that common, since a lot of themwould be written with context bounds instead.