-
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
Make Function
generic, and Function<Record, Type>
denote a function type.
#2478
Comments
Should the parameter list come before the return type? |
Hm, it's usually "Input/Output" but signatures are Also, is there an issue open for adding optional values to records? Is that the only difference left between records and parameter lists? |
@Levi-Lesches Great, thanks! |
Today, all closures implement the simple non-generic |
A record type syntax also can't express which named arguments are |
Is there a reason records don't copy the grammar/semantics of parameter lists? I see a lot in discussions that records are eventually meant to mirror parameter lists so I'm curious why they're not made to be equivalent from the beginning. Dart already has function subtyping logic (ie, |
Unless we have #2232. |
Records do significantly borrow from parameter lists. The record type grammar is essentially a subtype of the function type parameter list grammar. But it doesn't have any notion of optional positional or required named parameters since that isn't meaningful for record objects since you either have the fields or you don't. |
Won't they still be useful as a shorthand for abstract class VersionCode {
String format() { /* format as semver string */ }
}
abstract class SemVer {
VersionCode getVersion(int major, int minor, {required int? patch, required int? build});
}
abstract class OptionalBuild extends SemVer {
@override
VersionCode getVersion(int major, int minor, {required int? patch, int? build});
}
abstract class MajorMinor extends SemVer {
@override
VersionCode getVersion(int major, int minor, {int? patch, int? build});
} With records you could simplify the whole thing down into typedef SemVer = (int major, int minor, {required int? patch, required int? build});
typedef OptionalBuild = (int major, int minor, {required int? patch, int? build});
typedef MajorMinor = (int major, int minor, {int? patch, int? build});
extension on SemVer {
String format() { /* format as semver string */ }
} And then // 1.2.3+4
SemVer full = (1, 2, patch: 3, build: 4);
OptionalBuild noBuild = (1, 2, patch: 3); // build = null
MajorMinor majorMinor = (1, 2); // patch = build = null
print(full.format()); // v1.2.3+4
print(noBuild.format()); // v1.2.3
print(majorMinor.format()); // v1.2 Just like |
Why not let it be |
Wouldn't that conflict with a generic function? typedef Adder<T> = T Function<T>(T a, T b); |
If we wanted a type to abstract over function parameter signatures, we could just do introduce R Function(..., {...}) to represent a supertype of any function which returns That's probably better. If we want to match it to an unknown record type, so that we know that the record can be spread into the argument list, without knowing the record shape or parameter list shape precisely... Then it's probably precisely the kind of thing we shouldn't do. |
no, because you MUST have a parameter list if you define a return value |
Thats weird and doesnt make sense. The idea of having a record type for the function parameters is that we do have it. class Wrapper<R extends Record, T extends Something> {
Wrapper(this._fn);
final T Function(...R) _fn;
List<T> repeat(...R args, {int repeat = 0}) sync* {
for (final i = repeat; i >= 0; i--) {
yield _fn(...args);
}
}
Map<T> repeatMapped(...R args, {int repeat = 0}) {
return {
for (final e in repeat(...args, repeat: repeat))
e.something: e,
};
}
} suddenly we can change the return type, or even add parameters |
It could also be useful for storing parameters and comparing against parameters that are sent in later. |
If we change the declaration of the
Function
class toclass Function<P extends Record, R>
and special-caseFunction<(int, int), int>
to mean the function typeint Function(int, int)
, and allowingFunction<Record, T>
to represent any function with return typeT
, then we get a way to abstract over parameter list shape, while knowing the return type:Function<Record, int> f
is a function which returns anint
.The grammar is free since
Function<...>
must currently be part of the special formFunction<...>(...)
for a generic function type, it's a compile-time error to haveFunction<...>
not followed by a parenthesis.Not all parameter lists can be represented by records. A record cannot represent optional parameters. We can see methods with optional parameters as a kind of union type, and this syntax only produces the leaf types used by those unions.
I still think it will be very useful in some kinds of code (Even just making
Function.apply
have typeR apply<R>(Function<Record, R> f, List positionalArguments, [Map<Symbol, Object?>? namedArguments]))
would be an improvement, we could tell the return type from the invocation, without being able to represent the entire function type.Maybe this is no better than simply giving
Function
as single type argument, which is the return type, and not try to abstract over argument shapes.The text was updated successfully, but these errors were encountered: