-
Notifications
You must be signed in to change notification settings - Fork 452
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
RFC: Named arguments should not make explicit arguments implicit except for structure parameters #5397
Labels
awaiting-review
Waiting for someone to review the PR
P-medium
We may work on this issue if we find the time
RFC
Request for comments
Comments
It turns out the implementation I suggested here has a big drawback, which is that inserting placeholders for explicit structure parameters means that those structure parameters can't be provided using named arguments. Also, using named arguments for them would lead to those placeholders being shifted to the next explicit arguments! I see three options:
|
github-merge-queue bot
pushed a commit
that referenced
this issue
Sep 27, 2024
Recall that currently named arguments suppress all explicit parameters that are dependencies. This PR limits this feature to only apply to true structure projections, except in the case where it is triggered when there are no more positional arguments. This preserves the primary reason for generalizing this feature (issue #1851), while removing the generalized feature, which has led to numerous confusions (issue #1867). This also fixes a bug pointed out [on Zulip](https://leanprover.zulipchat.com/#narrow/stream/270676-lean4/topic/.40foo.20.28A.20.3A.3D.20bar.29.20_.20_/near/468564862) where in `@` mode, instance implicit parameter dependencies to named arguments would be suppressed unless the next positional argument was `_`. More detail: * The `NamedArg` structure now has a `suppressDeps : Bool` field. It is set to `true` for the `self` argument in structure projections. If there is such a `NamedArg`, explicit parameters that are dependencies to the named argument are turned into implicit arguments. The consequence is that *all* structure projections are treated as if their type parameters are implicit, even for class projections. This flag is *not* used for generalized field notation. * We preserve the suppression feature when there are no positional arguments remaining. This feature pre-dates the fix to issue #1851, and it is useful when combining named arguments and the eta expansion feature, since dependencies of named arguments cannot be turned into eta arguments. Plus, there are examples of the form `rw [lem (h := foo)]` where `lem` has explicit arguments that `h` depends on. * For instance implicit parameters in explicit mode, now `_` arguments register terminfo and are hoverable. * Now `..` is respected in explicit mode. This implements RFC #5397. The `suppressDeps` flag suggests a future possibility of a named argument syntax that can suppress dependencies.
Completed by #5283. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
awaiting-review
Waiting for someone to review the PR
P-medium
We may work on this issue if we find the time
RFC
Request for comments
Summary
Remove the general
anyNamedArgDependsOnCurrent
feature from the app elaborator that was added in a5ab59a and instead address #1851 by making specifically structure projection notation enable it for theself
named argument.Supersedes RFC #5386.
Motivation
The app elaborator has a feature (added in a5ab59a) where every explicit parameter that is depended upon by a named argument becomes an implicit parameter. We will call this feature argument suppression via named arguments for the purpose of this RFC.
There are two motivations for it.
The first motivation is that when there are no positional arguments remaining, every remaining parameter that is depended upon by a named argument is fully determined by that named argument, so there is no sense in requiring that such an argument be supplied. For example, given
Nat.le_of_lt : (a b : Nat) → (h : a < b) → a ≤ b
, one would be tempted to writeNat.le_of_lt (h := pf)
, and it would be inconvenient to need to supplya
andb
as well. Additionally, such arguments are not suitable for the eta expansion feature, where each missing explicit argument creates an "eta argument" — an argument that is lambda abstracted — since this would result in a type-incorrect term.The second motivation is to make projection notation for structures more uniform. For background, recall that for a structure such as
the field projection
S.f
has a type of the form{α β : Type} → (self : S α β) → α → β
. The parameters beforeself
are the structure parameters, which are always fully determined by the type ofself
. Similarly, for a class such asthe field projection
C.f
has a type of the form(α : Type) → {β : Type} → [self : C α β] → β
. Notice that in this case the first structure parameter is explicit since it cannot be inferred from any other explicit arguments or from the return type, and it is needed for typeclass inference of theself
parameter.For
x : S
, the projection notationx.f
is elaborated as if it wereS.f (self := x)
, using named arguments. Similarly, wheninst : C
, theninst.f
is elaborated as if it wereC.f (self := inst)
. Named arguments are the mechanism by which the elaborator provides a value forself
, which works even when it is an implicit argument.In issue #1851 it was noticed that elaborating
inst.f
asC.f (self := inst)
led to confusing behavior, since the structure parameters needed to still be supplied. This was fixed by generalizing the argument suppression via named arguments feature to apply to all arguments, even if there are still positional arguments to process. This addresses the issue because the structure parameters are always depended upon by theself
parameter, so they become implicit. The result is that forinst : C α β
the projectioninst.f
elaborates as@C.f α β inst
.However, this leads to other confusions. Named arguments in general suppress all explicit parameters depending on them, not just in the structure projection case. For example, in issue #1867 there is unintuitive behavior with using a named argument for an instance parameter. The general issue is that it is unclear to the user which parameters depend on implicit parameters — after all, they do not appear in the source text! — so it can be hard to predict what will happen. Furthermore when it does occur, the only mitigation when it is unwanted is to use named arguments for the suppressed explicit parameters.
The argument suppression via named arguments feature also has a counterintuitive interaction with explicit mode ("
@
mode"), pointed out on Zulip. The assumption of explicit mode is that each argument should be provided explicitly. Argument suppression via named arguments is however still enabled in explicit mode since the implementation of explicit mode is, essentially, that each parameter becomes explicit (with a special case for_
arguments for instance implicit parameters). Since they are explicit, argument suppression via named arguments applies. For an example counterintuitive behavior, in@C.f (self := inst)
theα
parameter becomes implicit since theself
parameter depends on it. We believe users would expect to need to write@C.f (self := inst) α
in explicit mode.Guide-level explanation
(Rough draft with key information) Projection notation (also known as field notation or dot notation) is a convenient object-oriented-like way to apply projection functions to a term. (... examples ...)
In the simplest case,
x.f
resolves toS.f x
, whereS
is a structure andf
is a field ofS
. Iff
is located in a parent structure, then Lean will automatically insert parent projections. (... examples ...)In particular, the notation
x.f
resolves asS.f (self := p x)
wherep
(optional) is the projection to the parent structureS
.Classes are a kind of structure as well, and projection notation can be used on class instances. However, classes are different from normal structures in that the
self
parameter of field projections is instance implicit rather than explicit. In this case,inst.f
resolves toC.f _ ... _ (self := inst)
, where there is one placeholder_
per explicit class parameter that isn't itself supplied using a named argument.In a section on named arguments:
Migration: In Lean pre-v4.XX.0, using named arguments would make every explicit argument it depends on to become implicit, even in explicit mode. This feature was made more restrictive, and you may need to insert some number of positional
_
or(_)
arguments to adapt to the change. Using_
should usually work, but(_)
may be necessary in explicit mode for instance implicit arguments that are non-canonical.Reference-level explanation
(... explanation of elaboration of normal applications ...)
When processing parameters, if there are no more positional arguments, then every subsequent explicit parameter...
Otherwise, the application is finalized.
When processing parameters, if there are more positional arguments, but the current parameter is explicit and there is a named argument tagged with
suppressDeps
that depends on it, then it is treated as an implicit argument.(... in section on projections ...) Projection notation
x.f
for structure projections resolves asS.f (self := x)
where the named argument is tagged withsuppressDeps
.Drawbacks
Users may be making use of this feature for named arguments. Kevin Buzzard for example was using it in explicit mode to avoid needing to supply "obvious"
_
s. It should be said that Kevin said he was making progress with which arguments to fill in next with guess-and-check, not intentionally.Floris van Doorn thinks that making class structure parameters implicit in projections can still lead to confusion.
Rationale
_
s, but it's also confusing when such a named argument application doesn't elaborate. At least the_
s only need to be added to the end.suppressDeps
solution for projection notation immediately improves named argument handling for explicit mode, making explicit mode more predictable. It keeps the invariant that each argument (be it positional or named) corresponds to one parameter. It is hard to predict which arguments are suppressed, especially ones that are originally implicit arguments as they do not generally appear in the syntax.self
parameter. However, users can writeC.f (self := inst)
(using the above exampleC
), which would surprisingly omit a parameter.pp.analyze
option can cause round-tripping issues because of this. Removing the feature fixes this. Note that projection notation notation is disabled for instances, so this RFC has no effect on pretty printing instance projections.In the issue #1867, Mario Carniero suggests another implementation that tries to preserve argument suppression via named arguments. That would entail a relatively large refactor since (1) the elaborator does not keep track of the actual order of named vs positional arguments, just an array for each and (2) the elaborator processes in parameter order, not argument order.
Unresolved questions and future possibilities
_
s for the explicit structure parameters._
s can be necessary for generalized projections? Or should generalized field notation compute what the "structure parameters" are and omit them for consistency?Community Feedback
This RFC comes from discussion on the Lean Zulip pointing out a bug in the interaction between how
_
is handled in explicit mode for instance implicit arguments and argument suppression via named arguments. The PR #5283 fixes it with the assumption that we want the feature to be enabled in explicit mode, but there was feedback to disable argument suppression via named arguments in explicit mode. This led to RFC #5386, and there was more feedback supporting disabling the feature more widely. There is some support to disable the feature entirely.Impact
Add 👍 to issues you consider important. If others benefit from the changes in this proposal being added, please ask them to add 👍 to it.
The text was updated successfully, but these errors were encountered: