Skip to content

Commit a989f77

Browse files
committed
implement binding application
1 parent f3d62b8 commit a989f77

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+903
-409
lines changed

CHANGELOG_NEXT.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ This CHANGELOG describes the merged but unreleased changes. Please see [CHANGELO
6767
customise the syntax of operator to look more like a binder.
6868
See [#3113](https://github.com/idris-lang/Idris2/issues/3113).
6969

70+
* Autobind and Typebind modifiers on functions, type constructors and
71+
data constructors allow the user to overload function application to
72+
look like a binder. See [#3582](https://github.com/idris-lang/Idris2/issues/3582)
73+
7074
* Fixity declarations without an export modifier now emit a warning in peparation
7175
for a future version where they will become private by default.
7276

CITATION.cff

Lines changed: 0 additions & 30 deletions
This file was deleted.
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
2+
.. _binding:
3+
4+
*********
5+
Binding Application
6+
*********
7+
8+
Binding application allows to overload the syntax of function application to better express the
9+
semantics of dependent types. Take for example the mathematical notation for Sigma-types:
10+
:math:`\Sigma x \in \mathbb{N} . Fin\ x` it binds the variable ``x`` and makes it available in the
11+
scope after the dot. Another example is the mathematical notation for ``forall``:
12+
:math:`\forall x \in \mathbb{N} | p x` it states that for all values ``x`` in the set of natural
13+
number, the property ``p`` holds.
14+
15+
Without any additional syntactic help those two types are only expressible using a lambda:
16+
17+
.. code-block:: idris
18+
record Sigma (a : Type) (p : a -> Type) where
19+
fst : a
20+
snd : p fst
21+
22+
record Pi (a : Type) (p : a -> Type) where
23+
fn : (x : a) -> p x
24+
25+
sigmaExample : Sigma Nat (\n => Vect n Int)
26+
27+
piExample : Pi Nat (\n => Vect n Int)
28+
29+
30+
Ideally, instead of relying on a lambda, we would like to write something closer to the original
31+
mathematical notation, binding application allows the following syntax:
32+
33+
.. code-block:: idris
34+
sigmaExample' : Sigma (n : Nat) | Vect n Int
35+
36+
piExample' : Pi (n : Nat) | Vect n Int
37+
38+
Binding Types
39+
=============
40+
41+
The first way to use binding application is to bind a type to a name. If
42+
we take our ``Sigma`` example again it means that we need to tell the compiler that the type
43+
constructor can be used with binding syntax. We do this by annotating the type declaration with
44+
the ``typebind`` keyword.
45+
46+
47+
.. code-block:: idris
48+
typebind
49+
record Sigma (a : Type) (p : a -> Type) where
50+
constructor MkSigma
51+
fst : a
52+
snd : p fst
53+
54+
55+
The type constructor can now be called with this syntax: ``Sigma (n : Nat) | Vect n Int``. And it reads
56+
"Sigma ``n`` of type ``Nat`` such that ``Vect n Int``. This reading is appropriated from the
57+
mathematical notation :math:`\forall x \in \mathbb{N} | p x` which reads the same.
58+
We can also annotate functions with the same keyword, for example the following alias is allowed:
59+
60+
.. code-block:: idris
61+
typebind
62+
∃ : (a : Type) -> (p : a -> Type) -> Type
63+
∃ = Sigma
64+
65+
In the implementation of this function we've used the ``Sigma`` type-constructor without any binding
66+
syntax. That is because marking something as binding does not stop them from using them with regular
67+
function application, for example the following is allowed:
68+
69+
.. code-block:: idris
70+
-- binding syntax from our alias
71+
s1 : ∃ (n : Nat) | Fin n
72+
s1 = ...
73+
74+
-- pointfree notation is allowed
75+
s2 : Sigma Nat Fin
76+
s2 = ...
77+
78+
s3 : (Nat -> Type) -> Type
79+
s3 = Sigma Nat -- partial application is allowed
80+
81+
82+
We've seen that you can annotate functions and type constructors, you can also annotate data-constructors. For example, to annotate a record constructor add the keyword before the `constructor` keyword:
83+
84+
.. code-block:: idris
85+
record Container where
86+
typebind
87+
constructor MkCont
88+
goal : Type
89+
solution : goal -> Type
90+
91+
ListCont : Container
92+
ListCont = MkCont (n : Nat) | Fin n
93+
94+
You can also annotate constructors for data:
95+
96+
.. code-block:: idris
97+
data Desc : Type where
98+
-- normal constructors
99+
One : Desc
100+
Ind : Desc -> Desc
101+
102+
-- binding data constructor
103+
typebind
104+
Sig : (s : Type) -> (s -> Desc) -> Desc
105+
106+
Binding application is a desugaring that takes an input of the form ``expr1 (name : type) | expr2``
107+
and maps it to a program ``expr1 type (\name : Type => expr2)``. Binding application will disambiguate
108+
between binding and non-binding calls for example, the following works:
109+
110+
.. code-block:: idris
111+
namespace E1
112+
export
113+
record Exists (a : Type) (p : a -> Type) where
114+
115+
namespace E2
116+
export typebind
117+
record Exists (a : Type) (p : a -> Type) where
118+
119+
ok : Exists (n : Nat) | Vect n a
120+
121+
122+
The compiler will automatically pick ``Exists`` from ``E2`` because the other one is not marked as
123+
binding. However, when using regular function application, the call must be disambiguated:
124+
125+
.. code-block:: idris
126+
failing
127+
noOK : Exists Nat Fin
128+
129+
unambiguous : E1.Exists Nat Fin
130+
131+
132+
133+
Binding Arbitrary Values
134+
========================
135+
136+
Sometimes, we still want to using binding application syntax, but we are not binding a type. In those
137+
cases you want to use the ``autobind`` feature to infer the type of the value being bound. The keyword
138+
works in the same places as the ``typebind`` keyword, that is:
139+
140+
On type constructors
141+
142+
.. code-block:: idris
143+
autobind
144+
record StringProp : (str : String) -> (String -> Type) -> Type where
145+
146+
autobind
147+
data MaybeProp : (m : Maybe a) -> (a -> Type) -> Type where
148+
149+
On data constructor:
150+
151+
.. code-block:: idris
152+
153+
on functions:
154+
155+
.. code-block:: idris
156+
autobind
157+
for : (List a) -> (a -> IO ()) -> IO ()
158+
for ls fn = traverse fn ls >> pure ()
159+
160+
161+
Instead of using ``:`` as a separator, we use ``<-`` and so the last function can be used this way:
162+
163+
.. code-block:: idris
164+
main : IO ()
165+
main = for (x <- ['a' .. 'z']) |
166+
putCharLn x
167+
168+
The desguaring works in a similar way except that the type of the argument in the lambda has to be
169+
inferred. ``expr1 (name <- expr2) | expr3`` desugars to ``expr1 expr2 (\name : ? => expr3)``.
170+
171+
Like typebind, you can use regular function application and partial application with ``autobind``
172+
definitions.

libs/contrib/Text/Parser.idr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module Text.Parser
22

33
import Data.Bool
4-
import public Data.Nat
4+
import Data.Nat
55
import public Data.List1
66

77
import public Text.Parser.Core

src/Compiler/Common.idr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ getMinimalDef (Coded ns bin)
156156
= MkGlobalDef fc name (Erased fc Placeholder) NatSet.empty NatSet.empty NatSet.empty NatSet.empty mul
157157
Scope.empty (specified Public) (MkTotality Unchecked IsCovering) False
158158
[] Nothing refsR False False True
159-
None cdef Nothing [] Nothing
159+
None cdef Nothing [] Nothing NotBinding
160160
pure (def, Just (ns, bin))
161161

162162
-- ||| Recursively get all calls in a function definition

src/Core/Binary.idr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import public Libraries.Utils.Binary
3030
||| version number if you're changing the version more than once in the same day.
3131
export
3232
ttcVersion : Int
33-
ttcVersion = 2025_08_16_00
33+
ttcVersion = 2025_08_20_00
3434

3535
export
3636
checkTTCVersion : String -> Int -> Int -> Core ()

src/Core/Context.idr

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import public Core.Name
1010
import Core.Options
1111
import public Core.Options.Log
1212
import public Core.TT
13+
import public Core.WithData
1314

1415
import Libraries.Utils.Binary
1516
import Libraries.Utils.Path
@@ -325,10 +326,11 @@ commitCtxt ctxt
325326
||| @vis Visibility, defaulting to private
326327
||| @def actual definition
327328
export
328-
newDef : (fc : FC) -> (n : Name) -> (rig : RigCount) -> (vars : Scope) ->
329+
newDef : {default NotBinding bind : BindingModifier} ->
330+
(fc : FC) -> (n : Name) -> (rig : RigCount) -> (vars : Scope) ->
329331
(ty : ClosedTerm) -> (vis : WithDefault Visibility Private) -> (def : Def) -> GlobalDef
330332
newDef fc n rig vars ty vis def
331-
= MkGlobalDef
333+
= MkGlobalDef
332334
{ location = fc
333335
, fullname = n
334336
, type = ty
@@ -352,6 +354,7 @@ newDef fc n rig vars ty vis def
352354
, namedcompexpr = Nothing
353355
, sizeChange = []
354356
, schemeExpr = Nothing
357+
, bindingMode = bind
355358
}
356359

357360
-- Rewrite rules, applied after type checking, for runtime code only
@@ -1372,6 +1375,7 @@ addBuiltin n ty tot op
13721375
, namedcompexpr = Nothing
13731376
, sizeChange = []
13741377
, schemeExpr = Nothing
1378+
, bindingMode = NotBinding
13751379
}
13761380

13771381
export

src/Core/Context/Context.idr

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ Show Def where
177177

178178
public export
179179
Constructor' : Type -> Type
180-
Constructor' = AddFC . WithName . WithArity
180+
Constructor' = AddFC . WithName . WithArity . AddMetadata Bind'
181181

182182
public export
183183
Constructor : Type
@@ -331,6 +331,7 @@ record GlobalDef where
331331
namedcompexpr : Maybe NamedDef
332332
sizeChange : List SCCall
333333
schemeExpr : Maybe (SchemeMode, SchemeObj Write)
334+
bindingMode : BindingModifier
334335

335336
export
336337
getDefNameType : GlobalDef -> NameType

src/Core/Context/Data.idr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ addData vars vis tidx (MkData con datacons)
101101
log "declare.data.parameters" 20 $
102102
"Positions of parameters for datatype" ++ show tyName ++
103103
": " ++ show paramPositions
104-
let tydef = newDef con.fc tyName top vars con.val (specified vis)
104+
let tydef = newDef {bind = con.bind} con.fc tyName top vars con.val (specified vis)
105105
(TCon con.arity
106106
paramPositions
107107
allPos
@@ -120,7 +120,7 @@ addData vars vis tidx (MkData con datacons)
120120
addDataConstructors tag [] gam = pure gam
121121
addDataConstructors tag (con :: cs) gam
122122
= do let conName = con.name.val
123-
let condef = newDef con.fc conName top vars con.val (specified $ conVisibility vis) (DCon tag con.arity Nothing)
123+
let condef = newDef {bind = con.bind} con.fc conName top vars con.val (specified $ conVisibility vis) (DCon tag con.arity Nothing)
124124
-- Check 'n' is undefined
125125
Nothing <- lookupCtxtExact conName gam
126126
| Just gdef => throw (AlreadyDefined con.fc conName)

src/Core/Core.idr

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,14 @@ namespace Binder
890890
traverse f (PLet fc c val ty) = pure $ PLet fc c !(f val) !(f ty)
891891
traverse f (PVTy fc c ty) = pure $ PVTy fc c !(f ty)
892892

893+
namespace BindingInfo
894+
export
895+
traverse : (a -> Core b) -> BindingInfo a -> Core (BindingInfo b)
896+
traverse f (BindType name type) = BindType <$> f name <*> f type
897+
traverse f (BindExpr name expr) = BindExpr <$> f name <*> f expr
898+
traverse f (BindExplicitType name type expr)
899+
= BindExplicitType <$> f name <*> f type <*> f expr
900+
893901
export
894902
mapTermM : ({vars : _} -> Term vars -> Core (Term vars)) ->
895903
({vars : _} -> Term vars -> Core (Term vars))

0 commit comments

Comments
 (0)