-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Abstract given seems useless #10954
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 let @nicolasstucki and @julienrf respond. They have been arguing strongly for abstract givens. |
I find abstract givens useful, indeed. Abstract definitions are a common means of abstraction (abstract methods and abstract type members). We can observe that in Scala 2 abstract implicit definitions are used (e.g. in cats, endpoints4s, akka, and maybe at other places that I’m not aware of). From a more fundamental perspective, there is a correspondence between parameters and abstract members. Both are means of abstractions, and you can refactor from one form to the other: trait WithMembers {
type A
val foo: A
} class WithParameters[A](val foo: A) However, if we remove abstract givens, this will make it impossible to refactor this: class Foo[A](using SemiGroup[A]) into that: trait Foo {
type A
given SemiGroup[A]
} You mentioned a workaround, which in my opinion is not only verbose, but very low-level and “mechanism-oriented” (as opposed to “intent-oriented”). I like how abstract givens removed half of the lines of this part of the compiler. |
That being said, if abstract givens are the only reason why we need the |
I agree with Julien. Also, even if I am not very fond of the empty
looks too much like an abstract def in my opinion, so reusing it for a slightly shorter syntax is not worth it IMO. Maybe we can find another solution to get rid of the |
We could define a structural instance like so without risk of confusion:
Only you (or Nicolas) can tell if there is a better way to express this. If not, then perhaps we need it after all. But in that case, maybe we could bring back a keyword such as
Edit : in this case, the problem is that given alias (and abstract given) on the one hand, and structural instances on the other hand, now are syntactically separate domains, and it would be difficult to implement an abstract given with a structural instance. |
|
After reflection, I still think the workaround is better, for separation of concerns. Abstraction is a thing, and context abstraction is something else. Having both conflated in the same construct is problematic. Especially since givens are meant to disentangle implicits from other concerns (among which is Happy new year. |
I believe one aspect is that the syntax given table: Table for a concrete given is objectionable since it looks like an abstract given. That was a common complaint when given table: Table with {} looks cryptic, but at least it's not misleading. A better way to write this is with an alias, even if it is slightly longer: given table: Table = Table() So no matter what we do, we probably should either outlaw given table: Table outright, or make it an abstract given. I was fielding the same "separation of concerns" argument as @rjolly to defend why there should be no abstract givens. On the other hand, since it is useful, easy to spec and implement, and the natural syntax is already available, why not? |
Then perhaps we should disable abstract and re-introduce given table extends Table For me, the main problem is that this mandates braces, right in the middle of a whole braceless effort: given table: Table with {} Edit: and it look abstract too, since there is no equal sign. |
And for anonymous? The root issue is that for givens |
I have the impression that the whole discussion here and the contributors thread is essentially trying to walk back to what we had before with |
For clarity, here is what it ends up looking like: given extends Ord[Int]:
def compare(x: Int, y: Int) = ...
given [T](using Ord[T]) extends Ord[List[T]]:
def compare(xs: List[T], ys: List[T]): Int = ... I think the reason However, IIRC you have argued that Scala declarations normally start with nouns ( Under that interpretation, // the following
given Obj extends Ord[Int] { ... }
// is conceptually similar to the following
object Obj extends Ord[Int] { ... }
// and if we had anonymous objects, we'd write
object extends Ord[Int] { ... }
// just like we would now write
given extends Ord[Int] { ... } So I think one will actually get used to this form quickly. Since it happens to solve many problems at once and has been proposed over and over by the community, why not give it a serious try? |
@LPTK I don't understand how this not a clear step backwards from what we had with |
I think given Ord[Int]:
def compare(x: Int, y: Int) = ... I agree this looks a bit weird, but colon is no better (and given [T : Ord] extends Ord[List[T]]:
def compare(xs: List[T], ys: List[T]): Int = ...
I think colon is okay for given alias (as it's a type ascription) and eventually abstract given for that matter. Structural instance OTOH would be better written with |
We can rationalize given extends C:
def f ... is revolting and a lot worse than given C with
def f... I have seen no argument why given C with {} is ugly and requires braces is not very strong because
So we might just never teach |
I might be misunderstanding something, but given extends C:
def f ... is not what I'm considering, which is: given C:
def f ... Edit: I realize that abstract accommodates nicely: given c: C // abstract
given C // no confusion since abstract cannot be anonymous
given c extends C // named instance |
But then you get the conflict between given C:
... and given c: C The |
I still think what we have is more regular: A given instance consists of
|
Well, |
The point of the current rule is that you can always write your given signature, and once that's done, decide on a continuation with a type alias or refinement. Whereas the |
Happy New Year. I'm getting an early start on my resolution to catch up with the latest research into given syntax. I think "closed" for an issue means "open for bike shedding"? My experience just now with the docs is that "given instance" is introduced first as somehow "most general." I suggest that "abstract given" is simplest. (I briefly understood the complaint about conflating structural abstraction with context abstraction, then my vision blurred and I no longer felt the difference.) It's especially simple because Then I can understand that I can define a given alias, which has familiar syntax. Defining a For the example, "implement Just pedagogically, it would be nice to read the use case (or As a footnote, I miss "optional |
Thanks Andrew for your insight. Martin, one last argument : it breaks the rule that (keyword, operator) starts an indented block and Edit: Contradicting myself, there's also refinement type: C & {
template body
} |
In reality, the optional part is object X {} In given-land, that would not be possible/allowed: given x extends Object {}
given x extends Object // structural part omitted
given Object {} // anonymous
given Object
given x {} // extends Object omitted -> collides with syntax above
given x So, what we have is that |
trait WithMembers {
type A
val foo: A
} class WithParameters[A](val foo: A) This is precisely what the current design does not allow: given WithMembers with {
type A = Int
val foo = 1
} given WithParameters[Int](1) // impossible, one has to append with {} |
I think I see what you mean. We would have to first parse:
, and then backtrack to:
, in case there's no equal sign. Is that impossible ? |
Abstract given were introduced in #10538 . But these seem useless, as one can always define an abstract field and use a given alias:
Would it be possible to drop this feature, as it forces us to add an uncanny
with
to givens even when no structural part is needed lampepfl/dotty-feature-requests#156The text was updated successfully, but these errors were encountered: