-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Decide on final syntax for given
parameters
#7151
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
Comments
I realize that going back to
Furthermore, |
Another problem with the current syntax is that: def foo given (x: A, y: B) = ...
def foo given (A, B) = ... Mean different things, the first takes two implicits of type |
Contrast with the parens-around-given approach: def foo(given x: A, y: B) = ...
def foo(given A, B) = ... Both of these mean the same thing, one has to write |
At the risk of restarting the infinite syntax wars: I think |
Another merit of putting given in parenthesis: it's much easier to see what's the return type of a method. Compare def dyni given QuoteContext: PV[Int] => Expr[Int] = dyn[Int] with def dyni(given QuoteContext): PV[Int] => Expr[Int] = dyn[Int] For myself, the return type of the second is immediately obvious, but my brain spins a while until I figure out the return type of the first. |
While we're on the subject of given instances, I think it's worth reconsidering the use of
My proposal:
|
Please consider changing extension ListOps[T](xs: List[T]) {
def second: T = xs.tail.head
def third: T = xs.tail.tail.head
} |
Regarding the syntax for import A.{ given as TC } For brainstorming, what about: import A.{ the[TC] } The downside of the proposal is that |
Giving the type of implicit instance after |
Also in OCaml, where it's used like Scala's |
Yeah I like the idea of replacing |
The idea to regularize the language by choosing given ListOrd[T](given X: T): Ord[List[T]] {
...
} it is very hard to see whether what we define is an anonymous given for a class The situation is not comparable with given parameters where we have to distinguish |
I don't think we need to scan that far, as soon as we see the the paren introducing This could also be reinforced by encouraging named givens to always start with a lower-case letter: given listOrd[T](given X: T): Ord[List[T]] {
...
} After all, the given generates both a class and a def, and the important part is that calling |
No, in fact the implemented type can be passed a given argument since what's on the right of the
That's a good convention. So that solves the human readability problem. The necessary lookahead in the parser is a solvable problem. That convinces me to go with Follow-on question: Should we also use |
I'm not so sure about that. As long as the convention is not enforced, some people will stray away from it, and it will make for very confusing code to read for others. The fact that in a definition, the To me, the obvious solution is to require an underscore in place of the name for anonymous instances. (This could be generalized for all other anonymous definitions.) Then distinguishing one from the other at a glance becomes easier: given ListOrd[T](given Ord[T]): Ord[List[T]] { ... }
given _: ListOrd[Int](given IntOrd) { ... } But as shown above, there's still the strange false symmetry between Requiring underscores there too is possible and would make sense, but may be deemed too cumbersome: given ListOrd[T](given _: Ord[T]): Ord[List[T]] { ... }
given _: ListOrd[Int](given IntOrd) { ... } |
Following the discussion in scala#7151, choose `:` instead of `as` for given clauses.
given Ord[Int] { ... } I believe this form reads the best i.e. the form where instance type comes right after given Ord[List[T]] where [T](given Ord[T]) as listOrd { ... } where least important part such as an identifier comes last. |
Good point. In fact, I hadn't really considered that we have both given aliases and given instances in my proposal above, I fear the subtle syntactic differences between them will lead to puzzlers, e.g.: Question: what's the difference between these two class Foo {
println("hi")
}
given Foo =
new Foo
given Foo {
new Foo
} Answer: The first creates a def of type It seems that the only way to prevent this sort of mistakes is to make the syntax more explicit and not just rely on a few symbolic tokens to convey intent. Since the documentation already talks about given aliases and given instances, we could reuse that vocabulary in the code too: given alias Foo = ...
given alias x: Foo = ...
given instance Foo { ... }
given instance x: Foo { ... } If this is felt to be too verbose, another alternative would be to use given Foo = ...
given x: Foo = ...
given instance Foo { ... }
given instance x: Foo { ... } Another way to make the distinction clearer would be to replace given Foo = ...
given x: Foo = ...
given extends Foo { ... }
given x extends Foo { ... } This makes it easy to associate given instances with the concept of class definition (if we keep |
Following the discussion in scala#7151, choose `:` instead of `as` for given clauses.
(edit: this was written under the impression given is used as adjective currently, if that doesn't hold it might sound slightly differently than intended) Didn't the originally proposed implied/given syntax work well? My impression was that it did. Just replace implied with any synonymous adjective other than 'given'? Or keep implied? Assumed, default, provided, implied, present, supplied, latent; doesn't really matter, no point bikeshedding over the specific word too much, so long as it is not 'given', to avoid the confusion. Not much would change either, neither compared to the original nor the current proposal. (Also I get the desire to have a noun here but last time when I tried to find a suitable one... I can't think of any specific ones that would fit. OTOH, I got half a dozen or so more or less fitting adjectives under a minute) |
Following the discussion in scala#7151, choose `:` instead of `as` for given clauses.
What about using a verb for declaration of the instances? In this line you essentially give ListOrd[T] as Ord[List{T]] given Ord[T] { ... } You can of course replace it with another word, provide ListOrd[T] as Ord[List{T]] provided Ord[T] { ... } I find my proposed syntax a lot more readable, and so far I feel like I have seen too many suggestions not addressing the biggest issue with |
I strongly agree with @heksesang that Scala should have a pair of keywords here, one for providing, one for consuming implicit value or type, like |
Agreed. That implicit listOrd[T] (implied Ord[T]): Ord[List[T]] { ... }
foo(x)(explicit y) give listOrd[T] (given Ord[T]): Ord[List[T]] { ... }
foo(x)(give y) canonize listOrd[T] (canonical Ord[T]): Ord[List[T]] { ... }
foo(x)(canonize y) |
I wasn't really a fan of using a verb, but these matching (imperative) verb / adjective pairs look the most intuitive to me so far. Also works with Easier to describe and discuss than I thought too, I guess if you Edit: Of the suggestions mentioned |
We have seen widespread consensus for the scheme that was merged in #7210. Widespread in the sense that everyone who has looked at it so far liked it, which is quite differences from experience with previous versions. |
I have had detailed look at a current state of |
Why not something like: |
It seems we have implicit consensus on
given
as the new name for implicit instances. It has a lot more support than its alternativedelegate
. So, let's take that as a given 😉given
is a good name for instances, but it does have a potential problem thatgiven
parameters andgiven
instances are too easily confused. The example that made this painfully clear for me was in #7056, where we find:At first, I could make neither heads nor tails of it and thought that the parser was faulty. Then I realized that the second
given
is a parameter to the first! Sure, it should have been indented but still... The syntax is dangerously misleading.One way to fix this is to choose different names for introducing parameters and instances. I.e., go back to
delegate
. Another way to fix it is to putgiven
under parentheses, thereby using the standard way of expressing parameter dependencies. I.e. it would beinstead of
This idea, originally proposed by @smarter, is elaborated in #7150 (docs only, no implementation).
One advantage is that it generalizes readily to implicit function types and literals. These would be
Another advantage is that it makes it possible to have normal parameters after given parameters, something we stopped allowing because the original syntax was so confusing.
A possible downside is in the application of multiple given arguments in one argument list. That would look like
instead of
f given (a, b)
The first syntax takes some getting used to, I think.
Another possible option is to keep
given
for instance definitions, but use something else for implicit parameters.where
was suggested by @milessabin. But any solution has to work in all of the following cases:where
works OK for the first two, but not for the last two.The text was updated successfully, but these errors were encountered: