Skip to content

Commit 64ddaa9

Browse files
committed
documenting polymorphism
1 parent 9b4a384 commit 64ddaa9

File tree

6 files changed

+224
-2
lines changed

6 files changed

+224
-2
lines changed

doc/main/contracts.html

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ <h1 class="title">Contracts.js Documentation</h1>
149149
<li><a href="#or"><span class="toc-section-number">4.5.1</span> <code>or</code></a></li>
150150
</ul></li>
151151
<li><a href="#naming-contracts"><span class="toc-section-number">4.6</span> Naming Contracts</a></li>
152+
<li><a href="#parametric-polymorphism"><span class="toc-section-number">4.7</span> Parametric Polymorphism</a></li>
152153
</ul></li>
153154
<li><a href="#faq"><span class="toc-section-number">5</span> FAQ</a><ul>
154155
<li><a href="#do-i-have-to-use-macros"><span class="toc-section-number">5.1</span> Do I have to use macros?</a></li>
@@ -335,6 +336,71 @@ <h2 id="naming-contracts"><span class="header-section-number">4.6</span> Naming
335336

336337
@ (NumId, Num) -&gt; Num
337338
function (f, x) { return f(x); }</code></pre>
339+
<h2 id="parametric-polymorphism"><span class="header-section-number">4.7</span> Parametric Polymorphism</h2>
340+
<p>Note: requires proxies (so use Firefox out of the box or Chrome/V8/node with the <code>--harmony</code> flag).</p>
341+
<p>Parametric polymorphic functions can be defined using <code>forall</code>:</p>
342+
<pre class="js"><code>@ forall &lt;name (,) ...&gt; &lt;contract&gt;</code></pre>
343+
<p>Where each <code>name</code> is a contract variable to be bound in <code>contract</code>. For example, the identity function is defined as:</p>
344+
<pre class="js"><code>@ forall a (a) -&gt; a
345+
function id(x) { return x; }</code></pre>
346+
<p>The contract enforces the invariant that for all types, the value applied to <code>id</code> will be returned from the function. If the function does not obey this invariant a contract violation will be triggered:</p>
347+
<pre class="js"><code>@ forall a (a) -&gt; a
348+
function const5(x) { return 5; }
349+
350+
const5(10);</code></pre>
351+
<p>will throw the error:</p>
352+
<pre style="color:red">
353+
const5: contract violation
354+
expected: an opaque value
355+
given: 5
356+
in: in the type variable a of
357+
the return of
358+
(a) -> a
359+
function const5 guarded at line: 2
360+
blaming: function const5
361+
</pre>
362+
363+
<p>A key idea of parametric polymorphism is that a function cannot inspect the value of a polymorphic type (otherwise it doesn't really work &quot;forall&quot;). For example, the <code>inc_if_odd</code> function behaves like the identity function unless its argument is odd, which violates the parametric invariant:</p>
364+
<pre class="js"><code>@ forall a (a) -&gt; a
365+
function inc_if_odd(x) {
366+
if (x % 2 !== 0) {
367+
return x + 1;
368+
}
369+
return x;
370+
}</code></pre>
371+
<p>So, attempting to invoke <code>inc_if_odd(100)</code> will throw the error:</p>
372+
<pre style="color:red">
373+
inc_if_odd: contract violation
374+
expected: value to not be manipulated
375+
given: 'attempted to inspect the value'
376+
in: in the type variable a of
377+
the 1st argument of
378+
(a) -> a
379+
function inc_if_odd guarded at line: 2
380+
blaming: function inc_if_odd
381+
</pre>
382+
383+
<p>Note that there are a couple of operations on values that contracts.js cannot currently guard against (<code>typeof</code> in particular).</p>
384+
<p>Polymorphic contracts also do contract inference. So, if you have a polymorphic array, contracts.js will check that the array is homogeneous:</p>
385+
<pre class="js"><code>@ forall a ([...a]) -&gt; [...a]
386+
function arrayId(l) {
387+
return l;
388+
}
389+
arrayId([1, 2, &quot;three&quot;]);</code></pre>
390+
<p>This infers that the <code>a</code> should be a <code>Num</code> for this application of <code>arrayId</code> and then throws and error when it discovers <code>&quot;three&quot;</code>:</p>
391+
<pre style="color:red">
392+
arrayId: contract violation
393+
expected: (x) => typeof x === 'number'
394+
given: 'three'
395+
in: in the type variable a of
396+
the 2nd field of
397+
the 1st argument of
398+
([....a]) -> [....a]
399+
function foo guarded at line: 2
400+
blaming: (calling context for arrayId)
401+
</pre>
402+
403+
<p>Contract inference is currently done with simple <code>typeof</code> checks.</p>
338404
<h1 id="faq"><span class="header-section-number">5</span> FAQ</h1>
339405
<h2 id="do-i-have-to-use-macros"><span class="header-section-number">5.1</span> Do I have to use macros?</h2>
340406
<p>No, as a matter of fact. If you'd like to just use the library in vanilla JavaScript you can. Load contracts.js and then use the <code>guard</code> function:</p>

doc/main/contracts.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,9 @@ blaming: the contract of foo
215215

216216
If you are familiar with contract research, this is the [indy](http://www.ccs.neu.edu/racket/pubs/popl11-dfff.pdf) semantics.
217217

218+
219+
220+
218221
## Object Contracts
219222

220223
Object contracts are built using familiar object literal syntax:
@@ -358,6 +361,109 @@ this, you can use `let` after the `@` symbol:
358361
function (f, x) { return f(x); }
359362
```
360363

364+
## Parametric Polymorphism
365+
366+
Note: requires proxies (so use Firefox out of the box or
367+
Chrome/V8/node with the `--harmony` flag).
368+
369+
Parametric polymorphic functions can be defined using `forall`:
370+
371+
```js
372+
@ forall <name (,) ...> <contract>
373+
```
374+
375+
Where each `name` is a contract variable to be bound in `contract`.
376+
For example, the identity function is defined as:
377+
378+
```js
379+
@ forall a (a) -> a
380+
function id(x) { return x; }
381+
```
382+
383+
The contract enforces the invariant that for all types, the value
384+
applied to `id` will be returned from the function. If the function
385+
does not obey this invariant a contract violation will be triggered:
386+
387+
```js
388+
@ forall a (a) -> a
389+
function const5(x) { return 5; }
390+
391+
const5(10);
392+
```
393+
394+
will throw the error:
395+
396+
<pre style="color:red">
397+
const5: contract violation
398+
expected: an opaque value
399+
given: 5
400+
in: in the type variable a of
401+
the return of
402+
(a) -> a
403+
function const5 guarded at line: 2
404+
blaming: function const5
405+
</pre>
406+
407+
A key idea of parametric polymorphism is that a function cannot
408+
inspect the value of a polymorphic type (otherwise it doesn't really
409+
work "forall"). For example, the `inc_if_odd` function behaves like
410+
the identity function unless its argument is odd, which violates the
411+
parametric invariant:
412+
413+
```js
414+
@ forall a (a) -> a
415+
function inc_if_odd(x) {
416+
if (x % 2 !== 0) {
417+
return x + 1;
418+
}
419+
return x;
420+
}
421+
```
422+
423+
So, attempting to invoke `inc_if_odd(100)` will throw the error:
424+
425+
<pre style="color:red">
426+
inc_if_odd: contract violation
427+
expected: value to not be manipulated
428+
given: 'attempted to inspect the value'
429+
in: in the type variable a of
430+
the 1st argument of
431+
(a) -> a
432+
function inc_if_odd guarded at line: 2
433+
blaming: function inc_if_odd
434+
</pre>
435+
436+
Note that there are a couple of operations on values that contracts.js
437+
cannot currently guard against (`typeof` in particular).
438+
439+
Polymorphic contracts also do contract inference. So, if you have a
440+
polymorphic array, contracts.js will check that the array is homogeneous:
441+
442+
```js
443+
@ forall a ([...a]) -> [...a]
444+
function arrayId(l) {
445+
return l;
446+
}
447+
arrayId([1, 2, "three"]);
448+
```
449+
450+
This infers that the `a` should be a `Num` for this application of
451+
`arrayId` and then throws and error when it discovers `"three"`:
452+
453+
<pre style="color:red">
454+
arrayId: contract violation
455+
expected: (x) => typeof x === 'number'
456+
given: 'three'
457+
in: in the type variable a of
458+
the 2nd field of
459+
the 1st argument of
460+
([....a]) -> [....a]
461+
function foo guarded at line: 2
462+
blaming: (calling context for arrayId)
463+
</pre>
464+
465+
Contract inference is currently done with simple `typeof` checks.
466+
361467
# FAQ
362468

363469
## Do I have to use macros?

examples/polymorphic-id.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import @ from "contracts.js"
2+
3+
@ forall a (a) -> a
4+
function inc_if_odd(x) {
5+
if (x % 2 !== 0) {
6+
return x + 1;
7+
}
8+
return x;
9+
}
10+
11+
inc_if_odd(100);

examples/polymorphic-inference.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import @ from "contracts.js"
2+
3+
@ forall a ([...a]) -> [...a]
4+
function arrayId(l) {
5+
return l;
6+
}
7+
arrayId([1, 2, "three"]);

js/app.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,20 @@ var examples = [
3333
title: "Dependent Contracts"
3434
},
3535
{
36-
id: 4,
36+
id: 5,
3737
file: "dependent-indy.js",
3838
title: "Dependent Contracts with Indy Blame"
39-
}
39+
},
40+
{
41+
id: 6,
42+
file: "polymorphic-id.js",
43+
title: "Polymorphic Identity"
44+
},
45+
{
46+
id: 7,
47+
file: "polymorphic-inference.js",
48+
title: "Polymorphic Inference"
49+
},
4050
];
4151
var contractModulePromise = Ember.$.ajax("macros/index.js", {
4252
dataType: "text"

test/test_contracts.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,28 @@ in: in the type variable c of
621621
(a, b, (a, b) -> c) -> c
622622
function bad_foo guarded at line: 609
623623
blaming: function bad_foo
624+
`
625+
})
626+
627+
it("should work for inc if odd", function() {
628+
@ forall a (a) -> a
629+
function inc_if_odd(x) {
630+
if (x % 2 !== 0) {
631+
return x + 1;
632+
}
633+
return x;
634+
}
635+
636+
blame of {
637+
inc_if_odd(100);
638+
} should be `inc_if_odd: contract violation
639+
expected: value to not be manipulated
640+
given: 'attempted to inspect the value'
641+
in: in the type variable a of
642+
the 1st argument of
643+
(a) -> a
644+
function inc_if_odd guarded at line: 629
645+
blaming: function inc_if_odd
624646
`
625647
})
626648

0 commit comments

Comments
 (0)