-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Generic syntax to replace provisional $
s
#565
Comments
We talked about the alternative of using
That seemed to suggest that the |
Alternate rule for the default in function declarations from @mconst (see this message in discord): "implicit parameters and struct parameters default to generic; everything else defaults to dynamic" This is simpler, and involves less need to carefully examine the code for readers to determine whether something is dynamic or generic when using the default. |
A modification of that last rule: In function declarations:
This way, when reading you can use the rule that all unmarked explicit parameters are |
I wonder how
Perhaps we don't need a |
@zygoloid , are you talking about |
Both, but I was specifically thinking about parameter lists. |
I think the four categories you list make sense as mutually exclusive alternatives. "Dynamic immutable value" is a good default in a parameter list. In a function body, it makes sense to spell that Thinking about this further, if we are going to allow you to declare names bound to "known-after-type-checking immutable values" inside structs and function bodies, I am tempted to suggest using the keyword |
There was some interesting discussion about this issue today in the open discussion session and I wanted to at least relay one aspect of it here (there were others but those might be better summarized by others). Specifically, we explored a couple of alternatives to the default-with-keyword-override approach suggested above. One alternative would be to make the default be indicated via syntax, and allow the syntax nesting to reflect explicit vs. deduced. This could be done with
Here, everything in We could also consider replacing Another alternative we looked at would instead of default-and-override, have consistent syntax everywhere. This of course comes at the cost of more syntax, and maybe less obvious syntax. The best suggested syntax during our brainstorming was to use
Here too, we could still keep moving towards Both of these are really designed as a contrast to the "pick a reasonable context-based default". There is a somewhat basic tradeoff in that space -- with good defaults, most of the time things "just work" and no extra ceremony is needed. For me, the amount of noise in the I personally find the last of these examples to be my favorite. There are two reasons why this works better for me. First, I find the immediate reminder of what layer at which the name is bound (which @zygoloid nicely describes the four layers of) helpful as a reader rather than relying on the context. That's probably pretty subjective, varies from person to person, and I have no strong indication of the distribution of preference here. Other than I suspect that this isn't the biggest of such subjective things. The other thing I like about it is that it somewhat pushes towards narrowly moving from "dynamic immutable" to "known-after-typechecking" or "known-before-typechecking". For me, that seems like a good thing. Moving from "dynamic mutable" to "dynamic immutable" seems like something we should encourage widely as it just restricts usage and enables useful implementations. But when we make it known at compile-time, we actually increases where we can use the name (in addition to narrowing how it binds). As such, encouraging it to be focused, to me, seems good. Some orthogonal points that came up:
Definitely interested in any other feedback on these options. |
Upon reflection, I don't think we need to distinguish the |
Clarifying my last comment: inside a function or struct body, the declarations require an initializer. There is very little value in requiring the user to write different keywords depending on whether the initializer is a template or generic value. I propose instead that in those cases, the user can simply write
|
A concern I have with the
This I think both adds clarity for the reader ("alert! this type is being passed in dynamically") and helps the compiler prevent the writer from accidentally passing in types dynamically. An alternative, depending on the resolution of #508 , would be to say:
This rule could be written more generally to non-parameters as well. In cases where a parameter could match either a type or non-type value, the
|
Having discussed this further, I'm going to say we don't urgently need dynamic types at all. We can definitely postpone those decisions until we actually are talking about the design space of dynamic types. |
FWIW, I'm pretty happy initialling rejecting the use of dynamic parameters as types (and as early as possible) so that as @josh11b indicates we can follow-up with a more cohesive design there. I agree we shouldn't make it easy or very subtle to accidentally get something that looks like a normal type or a generic but in reality has a dynamic/runtime implementation strategy with associated overhead. While that has really interesting use cases, it should be pretty clear in the source how you got there so that the wildly different tradeoff compared to generics is clear and unsurprising. |
FWIW, the leads discussions have converged here around using The primary rationale for not pursuing the contextual-default with override keywords approach was that we had a lot of concerns around whether the defaults would be visually consistent and easily recognized by readers. One aspect that significantly amplified these concerns is that we don't see any really good ways to make the different contexts have punctuation that directly reflects their defaults. We looked at a bunch of the options here, and none were appealing. While that wasn't the only concern with the defaults, I think it was the most clear-cut issue that pushed us away and towards a more simple but more noisy syntactic approach like the One concern with Another approach that has been considered is relying on the type itself more heavily. Discussions here have ranged from using the type to key a default with override keywords (much like the prior discussion around a default-with-override-keywords) to actually embedding the generic-ness into the type system itself such that it would be possible to perfectly forward generic-ness. These options are ones that seem like they would need some significant work to fully explore (especially how far if at all to go towards embedding in the type system). Doing this seems like the right thing to do if the But for now, we suggest using the |
I am going to restate my understanding to make sure we are all on the same page and in agreement:
My understanding of the alternatives considered, from most preferred to least:
|
Yep!
I think these two were both vaguely considered together, and also including other variations on these themes. But I don't want future readers to over index on the specific, concrete expressions here, but more the overall direction. I would include in any consideration of this direction whether or not generic-ness might be fundamentally part of the type system.
|
With Josh's more concrete summary (thanks!) I'm going to close this out as resolved for now. |
This implements decision #565 to use `T:! Type` to declare generic parameters, and `template T:! Type` for template parameters. Co-authored-by: Richard Smith <[email protected]>
This implements decision #565 to use `T:! Type` to declare generic parameters, and `template T:! Type` for template parameters. Co-authored-by: Richard Smith <[email protected]>
(Cross posting with #1425) In my own opinion, the first time I saw Have we discussed other symbols on the keyboard? [T:# Comparable & Movable]
[T:% Comparable & Movable]
[T:^ Comparable & Movable]
[T:@ Comparable & Movable] My personal reasons:
|
Let me copy my comment from the discussion thread. Link: Let me give you a maybe final answer. In the code |
I think this discussion belongs better in #1425 than here, since this issue is old and closed. |
Right now, I am using this provisional syntax in my generics proposals:
x: T
- dynamic parameterx:$ T
- generic parameterx:$$ T
- template parameterIn a discussion with @mconst , we thought that this alternative would be better:
dynamic x: T
- dynamic parametergeneric x: T
- generic parametertemplate x: T
- template parameterx: T
- dynamic or generic parameter based on contextThe rules for determining whether
x: T
was dynamic or generic would be:dynamic
generic
generic
dynamic
An example of this last rule:
Benefits of this approach:
:
by itself is usually what you want. Particularly this syntax avoids asking the user to know that they have to do something different for type parameters.The main downside of this approach is that it is context sensitive, but the scope of the context is limited to the current declaration. It also uses more characters in cases where you need to specify a keyword.
We would still need a syntax for declaring associated types and constants in an interface body, I propose
const name: type
, optionally followed by an= default
.I'm hesitant to use
var
here (though we could, with ageneric
default) becausevar
would have a different interpretation when implementing this interface in astruct
definition, and generally elsewherevar
would default todynamic
.The text was updated successfully, but these errors were encountered: