-
Notifications
You must be signed in to change notification settings - Fork 57
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
Optional and named arguments #1991
Comments
Would it be correct to think about optional arguments in the following way?
If so, we could use the following syntax:
This syntax has the advantages:
About restriction 2. Is the behavour that you have in mind as follows?
It will be desugared into
That is, when we pass an explicit named argument, we assume that arguments that come before it, have been omitted. Another example, If we have
Then, it desugars to
|
Yes, I think that's actually a better way to think about it. Then restrictions 1 and 2 are not needed because there is no ambiguity anymore. And for 3 the existing implicit syntax needs to be extended instead of creating a new syntax. The only disadvantage is that the default values are not really a part of the type, but we're putting them in the type signature, because we have no better place for. Though maybe it makes sense to put them there, because the signature should provide info on how to call the function, including which values will be provided for omitted arguments. Strictly speaking, being implicit itself is something "on top" of the type system already.
Named arguments are actually a separate issue from optional ones. When optional arguments are not implicit then there is an ambiguity between whether the function is partially applied or the default values should be substituted, e.g., should
desugar to
or to the partial application (where the argument
If all optional arguments are implicit, then it's not ambiguous. As for named arguments, I think they are a useful syntactic convenience. They can but don't have to be implicit/optional. When they are specified in an application by naming them, it should be possible to specify them in any order, e.g.:
should desugar to
In Haskell, the lack of named arguments is usually circumvented by declaring a separate record to capture all "named" arguments of a function if it needs many of them. This works less well with type dependencies, and in general is a bit less convenient than a proper named arguments syntax. |
Then to make named lambdas consistent we would need to write: go{acc1 := []}{acc2 := 0}@\{
[] := (acc1, acc2)
| (x :: xs) := go {x :: acc1} {acc2 + 1} xs
}; That's perhaps better, because it makes it clear that the |
Exactly, so only implicit arguments should be allowed to have default values.
I am not sure about this. Since we already have Pi types (currently only over |
The order in which you provide the arguments doesn't matter as long as the compiler can unambiguously reshuffle them to their "proper" order before type-checking. The names enable this reshuffling. The benefit is that you want to refer to a named argument by its name, and you don't want to remember or care about the order - that's why you named the argument in the first place. When you're using named arguments seriously, you'll have double-digit numbers of them for some functions, as you sometimes have double-digit numbers of fields in a record. If you have good error messages that clearly indicate the order, you can reduce the reordering pain, but why generate the pain if it's not necessary? I see this as analogous to ordering the fields in a record. They do have an order (which is significant for type-checking with dependent records), but when you provide the field values by naming the fields you don't need to list them in their original order, which is often arbitrary (disregarding type-checking). I think few languages with records enforce the order of record fields. Because it's inconvenient. |
Yes, right. If you allow them to be explicit you would have to restore restriction 2 to resolve the ambiguity. |
We've discussed offline the idea of passing arguments in unordered groups. E.g. if we have the following definition (I omit the types because they are not relevant):
It would be possible to write:
Note that groups must still be ordred with respect to other regular arguments or other groups, so the following is not acceptable
|
- closes #1991 This pr implements named arguments as described in #1991. It does not yet implement optional arguments, which should be added in a later pr as they are not required for record syntax. # Syntax Overview Named arguments are a convenient mehcanism to provide arguments, where we give the arguments by name instead of by position. Anything with a type signature can have named arguments, i.e. functions, types, constructors and axioms. For instance, if we have (note that named arguments can also appear on the rhs of the `:`): ``` fun : {A B : Type} (f : A -> B) : (x : A) -> B := ... ; ``` With the traditional positional application, we would write ``` fun suc zero ``` With named arguments we can write the following: 1. `fun (f := suc) (x := zero)`. 2. We can change the order: `fun (x := zero) (f := suc)`. 3. We can group the arguments: `fun (x := zero; f := suc)`. 4. We can partially apply functions with named arguments: `fun (f := suc) zero`. 5. We can provide implicit arguments analogously (with braces): `fun {A := Nat; B := Nat} (f := suc; x := zero)`. 6. We can skip implicit arguments: `fun {B := Nat} (f := suc; x := zero)`. What we cannot do: 1. Skip explicit arguments. E.g. `fun (x := zero)`. 2. Mix explicit and implicit arguments in the same group. E.g. `fun (A := Nat; f := suc)` 3. Provide explicit and implicit arguments in different order. E.g. `fun (f := suc; x := zero) {A := Nat}`.
We could consider adding optional arguments in general. This would generalise the accumulator arguments in recursive lambdas (issue #1638). Plus, optional arguments are useful independently.
But for them to fit into our syntax we would need to require the following.
Then it makes sense that for a lambda the optional arguments can be declared before the lambda, like with
go
below:Then inside the lambda we can have multiple pattern matching clauses. This generalises the accumulator arguments to recursive lambdas (issue #1638).
2. The optional arguments can only be provided in an application by explicitly naming them, to avoid ambiguity with partial application, e.g.,
go (acc1 := x :: acc1) (acc2 := acc2 + 1) xs
.3. Perhaps we should somehow indicate the optionality of arguments in the type? Otherwise, the types are confusing. For example:
The text was updated successfully, but these errors were encountered: