-
Notifications
You must be signed in to change notification settings - Fork 207
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
Should we have generic getters? #1622
Comments
I'd say no. Not unless you can have generic fields. The getter/field symmetry is the reason to have getters to begin with, otherwise they could just be nullary functions. That's a reason we don't already have generic getters. We could, like we have implicit instantiation of tear-offs or function calls, we could have had implicit instantiation of getter invocations if we wanted to. I'm more interested in generic operators, because operators have actual arguments that could benefit from being parameterized by type. Like |
On the semantics: Generics are referred to as "type arguments" because, just like regular arguments, they provide input to a function or type. To allow getters to have generics would be saying that getters can declare an input, which contradicts with distinction of getters and setters from regular functions. |
I would vote a safe cast operator rather than a generic getter, like |
@Levi-Lesches wrote:
True, a generic getter is a function from types to a value, so it does take an input. One consequence of this is that we probably won't have any stable generic getters. However, we're already considering the use of syntax like It is true that a language construct like |
I think it's just the semantic distinction that has me on the fence. From Effective Dart:
Generics are not data input, but they still do represent an input, namely that the return value changes with the type argument used. Functions are typically the ones that return different values with different inputs. |
If we want to allow you to omit parentheses from zero-argument function invocations, we can do so today. (We'd have to find a new syntax for tear-offs, but that's doable, say I don't see making |
I do appreciate the arguments against this feature on the grounds of semantic consistency, but nonetheless it is useful to have some syntax here. I ran into this exact issue with a CL I'm working on to convert between Dart and JS objects. Most of the JS objects map 1:1 to their Dart counterparts, but some conversion functions require a type argument, i.e. Now we're left in a situation where users have to write:
On the other hand, I definitely see @lrhn 's argument. I really like the suggestion of allowing users to omit parentheses from zero-argument functions, and TBH I actually kind of like Anyway, nothing concrete to add, other than this does occur, in real code, and hopefully some day we will have a solution here. |
Thanks @joshualitt, real world examples are very important! First, I'd like to retrace some of the arguments in this issue. I'm trying to understand the counterarguments, in particular the ones that we might paraphrase as 'getters should not receive any arguments (even type arguments) because then they aren't getters'. For instance: @lrhn wrote:
@Levi-Lesches wrote:
We can certainly say that they are functions/methods that receive one or more type arguments and no value arguments. Given that we do not have a problem with functions/methods that receive one or more value arguments, but no type arguments, why would it be a problem if we can support the converse? If it's really important to define these guys as functions/methods rather than getters then I'm sure we can come up with a method/function declaration syntax where it is indicated that this particular function does not take any parameters. However, this would give rise to questions like 'why can't I tear off this method?'. It is probably going to work more consistently and conveniently if we do make them a variant of getters: Hence, I'd prefer to support generic getters: There's nothing conceptually wrong with the notion of a function that receives type arguments, but not value arguments, and the technical realization of this concept as a generic getter has nice pragmatic properties. Returning to the concrete case, let's consider an expression like We already have expressions like It is of course possible to make a getter perform a very expensive computation with side effects, but it's considered good style to ensure that a getter invocation can be understood as a query about an object property, it shouldn't change the state of the receiver (visibly), and invocations shouldn't be expensive. It shouldn't be any harder for the author of a generic getter to have this style rule in mind than it is for the author of any other getter. A generic getter like Also, |
There is no computation that can be done with a generic getter, which cannot be done with a generic zero-argument function, and the latter can also be torn off. It's all about syntax. But syntax does matter, it'd all be lambda calculus otherwise. If we introduce generic getters, aka functions which can be called without an argument list, but can still be given a type argument, then as @eernstg says, we probably can't tear them off, not if we also want to be able to infer type arguments. Their function type would probably be a normal function type with zero arguments, it's just invocation that's specialized. Dynamic invocation gets tricky, but if we can do Then we'd probably also want generic setters, which is trickier, but |
Nice to see there is some recent discussion on this. This feature would make my life easier as Mockito maintainer. Currently Mockito has to override all methods of mocked class to accept nullable arguments since matchers like I do understand though that Mockito just abuses getters to get a nicer syntax for expectations, so it's a "not so realistic" example and we definitely don't want to break the language for just this use case... but since there are others and in general the feature doesn't look that crazy, I'd vote for implementing it. |
(@yanok, please do vote on the initial comment, it does matter ;-) Of course, one could say that this is all about getting rid of a mere It might be useful simply because it's more concise. Otherwise, the given functionality could have been provided via a getter previously, but we'd now like to make it context sensitive because the underlying software (and language, actually!) has evolved, and it would cause a lot of churn to use a nullary method because we'd have to add all those From a language design perspective, and when it comes to code readability, comprehensibility, and maintainability, I think there are cases where these context sensitive getters are at least as readable as the current approach. Here's an example using the void f(int? i) {...}
void main() {
num n = ...;
var i = n.as<int>; // Could throw.
var j = n.asOrNull<int>; // Won't throw, but is nullable.
f(n.as); // Can use type inference to cast to exactly the type that's needed.
f(n.asOrNull); // `f` accepts null, so we can avoid the potential throw.
// Using extension methods rather than getters. Certainly possible, but is it better?
f(n.as());
f(n.asOrNull());
// Relying on language primitives, no abstractions.
f(n as int); // No diagnostics if `int` is too strict by mistake.
f(n is int ? n : null); // The standard way to express `orNull`.
} |
Given that we plan to allow
name<type, arguments>
as an expression, do we then lift the restriction that getters cannot be generic?This could be convenient, e.g., for the expression of "cast" getters:
The text was updated successfully, but these errors were encountered: