Feature request: Allow for overloading function application (the 'whitespace' operator)
4e554c4c opened this issue · comments
Background
About a month ago, it was brought up on the fediverse that it should be possible to overload function application in agda. There is a use-case brought up in that thread, but I will mention another here
In agda-categories a NaturalTransformation
contains a family of maps (a function from objects to homs) called η
. This works well if you would like to refer to your natural transformation by η
. However, I often find myself using other natural transformations (such as the counit of an adjunction) refered to by other letters, such as ε
. Then, one must write things like adj.ε.η x
to refer to what one in mathematics would call adj.ε x
.
Implementation details
Several libraries, such as the 1lab, already have partial solutions to this. For example, the FunLike
typeclass allows users to write ε # x
, which resolves to the desired ε .η x
Under this proposal, the user would write
{-# BULTIN APP _#_ #-}
and then F x
would implicitly mean F # x
, which would find a suitable FunLike
instance for F
through typeclass search.
I don't think this reaction is appropriate; it's not April. This issue is a legitimate feature request, with prior art in other proof assistants and, more importantly, our very own (FROMNAT
, FROMNEG
, FROMSTRING
, do
notation, idiom brackets).
The reason I suspect this would be possible to implement without moving a mountain of code is that we (a) already have handling for deferring the checking of arguments when a function's type is unresolved and (b) already overload function application, in --cubical
, to support applying PathP
s.
The idea is simple: in checkArgumentsE
, if we're checking an application of a visible argument to a type that is definitely not a Pi (or Path), then we instead elaborate (f as # x) es
, where as
stands for the already-checked arguments, x:es
are the arguments-to-check, and _#_
is whatever appropriate builtin --- maybe BUILTIN TOFUNCTION
.
However, there are actually a couple implementation challenges regardless.
-
Agda is quite content to assume that any rigid symbol is the "target type" of aΠ
, even if the type is just aDef
, so it's actually pretty difficult to get into a situation where you'd get aShouldBePi
. For example:
Fixed, see below -
checkArgumentsE
and its callers assume that checking an application can only snoc newElim
s onto the end of the head term. The feature proposed here would require some shuffling, so thatcheckArgumentsE
would be allowed to do the "rotation" necessary to build the application of_#_
.The most sensible thing, IMO, would be to defunctionalise the continuation to
checkArguments
--- essentially storing the head term ± information about whether or not some parameters should be dropped. -
inferApplication
,checkArguments
etc. like to postpone instance constraints. If the user has implemented their_#_
using instance arguments, then it's probably a good idea to at least try solving constraints generated by elaboratingf # x
immediately.Using the 1Lab's
_#_
for an example, if we're checking e.g.f x y
, forf : T
withinstance app : Funlike T A (λ _ → B → C)
the only viable candidate, then solving theFunlike
constraint eagerly would allow us to know thatx : A
and(f # x) : B → C
. This would avoid postponing the checking ofx
and of the remaining argumentsy
.
Because this will be a more significant refactoring of checkArgumentsE
than I originally thought, I think it's probably a good idea to wait for some actual opinions for/against this feature before I spend my time implementing it. Needless to say, if we do go forward with this, I'm happy to put in the work.
This is the typeclass we use for this in the 1Lab:
record Funlike {ℓ ℓ' ℓ''} (A : Type ℓ) (arg : Type ℓ') (out : arg → Type ℓ'') : Type (ℓ ⊔ ℓ' ⊔ ℓ'') where
field
_#_ : A → (x : arg) → out x
infixl 999 _#_
open Funlike ⦃ ... ⦄ using (_#_) public
{-# DISPLAY Funlike._#_ p f x = f # x #-}
apply
: ∀ {ℓ ℓ' ℓ''} {A : Type ℓ} {B : A → Type ℓ'} {F : Type ℓ''}
→ ⦃ _ : Funlike F A B ⦄
→ F → (x : A) → B x
apply = _#_
Agda dev meeting 2024-03-20:
- Add this as experimental feature under a flag.
BUILTIN TOFUNCTION
, but do not fix an implementation of this in the builtin modules (yet).