agda / agda

Agda is a dependently typed programming language / interactive theorem prover.

Home Page:https://wiki.portal.chalmers.se/agda/pmwiki.php

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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 $\varepsilon_x$. Instead, it would be better to refer to this as 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.

déjà vu

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 PathPs.

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.

  1. Agda is quite content to assume that any rigid symbol is the "target type" of a Π, even if the type is just a Def, so it's actually pretty difficult to get into a situation where you'd get a ShouldBePi. For example:
    Fixed, see below

  2. checkArgumentsE and its callers assume that checking an application can only snoc new Elims onto the end of the head term. The feature proposed here would require some shuffling, so that checkArgumentsE 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.

  3. 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 elaborating f # x immediately.

    Using the 1Lab's _#_ for an example, if we're checking e.g. f x y, for f : T with instance app : Funlike T A (λ _ → B → C) the only viable candidate, then solving the Funlike constraint eagerly would allow us to know that x : A and (f # x) : B → C. This would avoid postponing the checking of x and of the remaining arguments y.

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.

@plt-amy

  1. This is probably a bug worth fixing regardless of whether or not we go ahead with the feature proposed here.

Indeed! To not side track the discussion here, I singled this out into:

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).