Skip to content

Commit ba71e14

Browse files
committed
add some documentation
1 parent e5301db commit ba71e14

File tree

1 file changed

+172
-0
lines changed

1 file changed

+172
-0
lines changed
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.

0 commit comments

Comments
 (0)