forked from disnet/contracts.coffee
/
index.html
563 lines (487 loc) · 37.2 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>disnet/contracts.coffee @ GitHub</title>
<link href='http://fonts.googleapis.com/css?family=Lobster|Gentium+Book+Basic' rel='stylesheet' type='text/css'>
<link href='documentation/contracts/styles/default.css' rel='stylesheet' type='text/css'>
<link href='documentation/contracts/styles/main.css' rel='stylesheet' type='text/css'>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-744645-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</head>
<body>
<a href="http://github.com/disnet/contracts.coffee"><img style="position: absolute; top: 0; right: 0; border: 0;" src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" /></a>
<div class="navigation">
<ul>
<li><a href="#basics">Basics</a></li>
<li><a href="#quickstart">Quick Start</a></li>
<li><a href="#resources">Resources</a></li>
<li><a href="#use">Use</a></li>
<li style="text-decoration: none;"> </li>
<li><a href="#simple">Simple</a></li>
<li><a href="#functions">Functions</a></li>
<li><a href="#objects">Objects</a></li>
<li><a href="#arrays">Arrays</a></li>
<li><a href="#operators">Operators</a></li>
<li><a href="#naming">Naming</a></li>
<li style="text-decoration: none;"> </li>
<li><span id="try_button">Try it now!</span></li>
<li style="text-decoration: none;"> </li>
<li style="text-decoration: none;"> </li>
<li>
<div style="position: absolute; left: 35px">
<a href="http://www.mozilla.org/">
<img style="height: 27px" src="documentation/images/moz-research.png">
</a>
</div>
</li>
</ul>
</div>
<div id="repl_container">
<div id="repl_editor">
<div id="repl_source_wrap">
<textarea id="repl_source"># Wrap the function in a contract that
# expects to be called with a number
# and will return a number as the result.
id :: (Num) -> Num
id = (x) -> x
# Call "use" to initialize the contract.
# See the use section of the docs
# for more details about "use".
id = id.use()
# Violates the contract by calling id
# with a string instead of a number.
id "foo"</textarea>
<div id="run_button">Run!</div>
<div id="source_label" class="label">CoffeeScript</div>
</div>
<div id="repl_result_wrap">
<div id="result_label" class="label">JavaScript</div>
<pre id="repl_results"></pre>
</div>
<div style="clear: both"></div>
</div>
<div id="blame">
<div id="error_label" class="label">Errors</div>
<pre></pre></div>
</div>
<div id="main">
<div id="container">
<h1><a href="http://github.com/disnet/contracts.coffee">contracts.coffee</a>
</h1>
<p>Contracts.coffee is a dialect of CoffeeScript with built-in support for contracts.</p>
<p>Contracts let you clearly—even beautifully—express how your code behaves, and free you from writing tons of boilerplate, defensive code.</p>
<p>You can think of contracts as <code>assert</code> on steroids.</p>
<span id='basics' />
<h2 id='basics'>Basics</h2>
<p>Here’s a simple example of a contract on a function:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>id</span> <span class='o'>::</span> <span class='nf'>(Num) -></span> <span class='nx'>Num</span>
<span class='nv'>id = </span><span class='nf'>(x) -></span> <span class='nx'>x</span>
</code></pre>
</div>
<p>This says that the <code>id</code> function should always be called with a number and will always return a number. It looks a lot like types (in fact the syntax looks a <em>lot</em> like Haskell) but unlike types, contracts are enforced at runtime in pure JavaScript.</p>
<p>If we try to use <code>id</code> incorrectly:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>id</span> <span class='s2'>"foo"</span>
</code></pre>
</div>
<p>The program throws an error, which displays lots of nice information telling us what we did wrong:</p>
<pre style='color: red'>
Error: Contract violation: expected <Num>,
actual: "foo"
Value guarded in: id_module:42
-- blame is on: client_code:104
Parent contracts:
(Num) -> Num
</pre>
<p>You can also put contracts on objects.</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>person</span> <span class='o'>::</span>
<span class='nv'>name: </span><span class='nx'>Str</span>
<span class='nv'>age: </span><span class='nx'>Num</span>
<span class='nv'>person =</span>
<span class='nv'>name: </span><span class='s2'>"Bertrand Meyer"</span>
<span class='nv'>age: </span><span class='mi'>42</span>
</code></pre>
</div>
<p>And arrays.</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>loc</span> <span class='o'>::</span> <span class='p'>[...</span><span class='nx'>Num</span><span class='p'>]</span>
<span class='nv'>loc = </span><span class='p'>[</span><span class='mi'>99332</span><span class='p'>,</span> <span class='mi'>23452</span><span class='p'>,</span> <span class='mi'>123</span><span class='p'>,</span> <span class='mi'>2</span><span class='p'>,</span> <span class='mi'>5000</span><span class='p'>]</span>
</code></pre>
</div>
<p>And in various combinations.</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>average</span> <span class='o'>::</span> <span class='nf'>({name: Str, age: Num}, [...Num]) -></span> <span class='nx'>Str</span>
<span class='nv'>average = </span><span class='nf'>(person, loc) -></span>
<span class='nv'>sum = </span><span class='nx'>loc</span><span class='p'>.</span><span class='nx'>reduce</span> <span class='nf'>(s1, s2) -></span> <span class='nx'>s1</span> <span class='o'>+</span> <span class='nx'>s2</span>
<span class='s2'>"#{person.name} wrote on average </span>
<span class='s2'> #{sum / loc.length} lines of code."</span>
</code></pre>
</div>
<p>Under the covers, contracts are really just normal functions that return <code>true</code> or <code>false</code>, so it’s really easy to roll your own by using the <code>!</code> operator to define new contracts.</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nv'>Even = </span><span class='nf'>(x) -></span> <span class='nx'>x</span> <span class='o'>%</span> <span class='mi'>2</span> <span class='o'>is</span> <span class='mi'>0</span>
<span class='nv'>Odd = </span><span class='nf'>(x) -></span> <span class='nx'>x</span> <span class='o'>%</span> <span class='mi'>2</span> <span class='o'>isnt</span> <span class='mi'>0</span>
<span class='nx'>addEvens</span> <span class='o'>::</span> <span class='nf'>(!Even) -></span> <span class='o'>!</span><span class='nx'>Odd</span>
<span class='nv'>addEvens = </span><span class='nf'>(x) -></span> <span class='nx'>x</span> <span class='o'>+</span> <span class='mi'>1</span>
</code></pre>
</div>
<p>In fact, since contracts are checked at runtime, they can enforce properties that static type systems can only dream of.</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nv'>Prime = </span><span class='nf'>(x) -></span> <span class='c1'># ...</span>
<span class='nx'>f</span> <span class='o'>::</span> <span class='nf'>(!Prime) -></span> <span class='o'>!</span><span class='nx'>Prime</span>
<span class='nv'>f = </span><span class='nf'>(x) -></span> <span class='nx'>x</span>
</code></pre>
</div>
<p>Eat your heart out, Haskell :)</p>
<span id='quickstart' />
<h2 id='quick_start'>Quick Start</h2>
<p>Here’s what you need to actually start working with contracts.coffee.</p>
<p>First, grab the source from github.</p>
<div class='highlight'><pre><code class='bash'>git clone git://github.com/disnet/contracts.coffee.git
<span class='nb'>cd </span>contracts.coffee
git submodule init
git submodule update
</code></pre>
</div>
<p>Install <a href='https://github.com/joyent/node/wiki/Installation'>Node.js</a> and then contracts.coffee.</p>
<div class='highlight'><pre><code class='bash'>sudo bin/cake install
</code></pre>
</div>
<p>Now, compile some coffee with contracts!</p>
<div class='highlight'><pre><code class='bash'>coffee -cC MyContractedScript.coffee
</code></pre>
</div>
<p>Note the <code>-cC</code> flag. The <code>-c</code> says to compile to JavaScript and <code>-C</code> enables contracts in the compiled JavaScript. If you don’t want contracts enabled (say in production) simply don’t include the <code>-C</code> flag.</p>
<p>You will also need to include <code>lib/loadContracts.js</code>. So the header of your HTML file will look something like:</p>
<div class='highlight'><pre><code class='html'>...
<span class='nt'><script </span><span class='na'>src=</span><span class='s'>"lib/loadContracts.js"</span>
<span class='na'>type=</span><span class='s'>"application/javascript"</span><span class='nt'>></script></span>
<span class='nt'><script </span><span class='na'>src=</span><span class='s'>"MyContractedScript.js"</span>
<span class='na'>type=</span><span class='s'>"application/javascript"</span><span class='nt'>></script></span>
...
</code></pre>
</div>
<p>Note that if you have an existing install of CoffeeScript, installing contracts.coffee will replace it. If you don’t want to give up the old CoffeeScript compiler you can just run contracts.coffee from its own directory:</p>
<div class='highlight'><pre><code class='bash'>bin/coffee -cC MyContractedScript.coffee
</code></pre>
</div>
<p>And finally, note that contracts.coffee requires some pretty new features of JavaScript to get its job done (in particular <a href='https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Proxy'>Proxies</a>) so it currently only works on Firefox 4+. Support for other JavaScript engines is coming soon.</p>
<p>This means that if you attempt to run your contracted CoffeeScript under node/V8:</p>
<div class='highlight'><pre><code class='bash'><span class='c'># note the -c flag is missing</span>
coffee -C MyContractedScript.coffee
<span class='c'># Compile error!!!</span>
</code></pre>
</div>
<p>You will get a bunch of errors. So just compile to JavaScript and run it in Firefox for now.</p>
<p>Note that since leaving off the <code>-C</code> flag will generate JavaScript code with absolutely no trace of contracts (the code is exactly what vanilla CoffeeScript would generate), you can easily set up a production build with contracts disabled that can run in any browser or JavaScript environment and a development/testing build with contracts enabled that you run in Firefox to help track down bugs.</p>
<span id='resources' />
<h2 id='resources'>Resources</h2>
<ul>
<li><a href='https://github.com/disnet/contracts.coffee/issues?sort=created&direction=desc&state=open'>Issue Tracker</a> <br /> For filing bugs, requesting features and changes.</li>
<li><a href='https://groups.google.com/forum/?hl=en#!forum/contractscoffee'>Google Group</a> <br /> For general discussion about contracts.coffee.</li>
</ul>
<span id='use' />
<h2 id='how_to_use'>How to Use</h2>
<p>In order to provide good error messages when things go wrong, contracts.coffee needs to know where contracted values are used in your code. What this means practically is that before you can use a value that has a contract on it, you must first <code>use()</code> it:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='c1'># Library.coffee</span>
<span class='nb'>window</span><span class='p'>.</span><span class='nx'>id</span> <span class='o'>::</span> <span class='nf'>(Num) -></span> <span class='nx'>Num</span>
<span class='nb'>window</span><span class='p'>.</span><span class='nv'>id = </span><span class='nf'>(x) -></span> <span class='nx'>x</span>
</code></pre>
</div><div class='highlight'><pre><code class='coffeescript'><span class='c1'># Main.coffee</span>
<span class='nv'>id = </span><span class='nb'>window</span><span class='p'>.</span><span class='nx'>id</span><span class='p'>.</span><span class='nx'>use</span><span class='p'>()</span>
<span class='nx'>id</span> <span class='mi'>4</span> <span class='c1'># ok</span>
<span class='nx'>id</span> <span class='s2'>"foo"</span> <span class='c1'># Contract Violation...</span>
</code></pre>
</div><pre style='color: red'>
Contract violation: expected <Num>, actual: "foo"
Value guarded in: Library:3
-- blame is on: Main:2
Parent contracts:
(Num) -> Num
</pre>
<p>The <code>use()</code> function does the work of dynamically setting up the right file names to display in error messages.</p>
<p>This is an unfortunate bit of syntax that we hope to do implicitly in the future with better compiler and module support.</p>
<p>In general it is advisable to only put contracts on module exports but if you want to use them in the same module simply call <code>use()</code> immediately.</p>
<p>Admittedly, in the example above finding the right file name is pretty trivial (we could have just inspected the stacktrace). To see the real power of recording the right names with <code>use()</code> consider the following example:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='lineno'>1</span> <span class='c1'># CheckingLibrary.coffee</span>
<span class='lineno'>2</span> <span class='nb'>window</span><span class='p'>.</span><span class='nx'>checkAge</span> <span class='o'>::</span> <span class='nf'>(Num) -></span> <span class='nx'>Bool</span>
<span class='lineno'>3</span> <span class='nb'>window</span><span class='p'>.</span><span class='nv'>checkAge = </span><span class='nf'>(age) -></span>
<span class='lineno'>4</span> <span class='c1'># make sure the age makes sense</span>
<span class='lineno'>5</span> <span class='nx'>age</span> <span class='o'>></span> <span class='mi'>0</span> <span class='o'>&&</span> <span class='nx'>age</span> <span class='o'><</span> <span class='mi'>150</span>
</code></pre>
</div><div class='highlight'><pre><code class='coffeescript'><span class='lineno'>1</span> <span class='c1'># Validator.coffee</span>
<span class='lineno'>2</span> <span class='nb'>window</span><span class='p'>.</span><span class='nx'>validateForm</span> <span class='o'>::</span> <span class='p'>(</span><span class='nf'>(Str) -></span> <span class='nx'>Bool</span><span class='p'>,</span> <span class='nx'>Str</span><span class='p'>)</span> <span class='o'>-></span> <span class='nx'>Bool</span>
<span class='lineno'>3</span> <span class='nb'>window</span><span class='p'>.</span><span class='nv'>validateForm = </span><span class='nf'>(checker, fieldName) -></span>
<span class='lineno'>4</span> <span class='nx'>checker</span> <span class='nx'>$</span><span class='p'>(</span><span class='nx'>fieldName</span><span class='p'>).</span><span class='nx'>val</span><span class='p'>()</span> <span class='c1'># failure is here </span>
</code></pre>
</div><div class='highlight'><pre><code class='coffeescript'><span class='lineno'>1</span> <span class='c1'># Main.coffee</span>
<span class='lineno'>2</span> <span class='nv'>checkAge = </span><span class='nb'>window</span><span class='p'>.</span><span class='nx'>checkAge</span><span class='p'>.</span><span class='nx'>use</span><span class='p'>()</span>
<span class='lineno'>3</span> <span class='nv'>validateForm = </span><span class='nb'>window</span><span class='p'>.</span><span class='nx'>validateForm</span><span class='p'>.</span><span class='nx'>use</span><span class='p'>()</span>
<span class='lineno'>4</span>
<span class='lineno'>5</span> <span class='nx'>$</span><span class='p'>(</span><span class='s2'>"form"</span><span class='p'>).</span><span class='nx'>submit</span> <span class='o'>-></span>
<span class='lineno'>6</span> <span class='c1'># checkAge takes Num but validateForm passes Str!</span>
<span class='lineno'>7</span> <span class='nx'>validateForm</span> <span class='nx'>checkAge</span><span class='p'>,</span> <span class='s2'>"#age"</span>
</code></pre>
</div>
<p>We get this contract violation:</p>
<pre style='color: red'>
Contract violation: expected <Num>, actual: "42"
Value guarded in: CheckingLibrary:2
-- blame is on: Main:3
Parent contracts:
(Num) -> Bool
</pre>
<p>Here we have a library that does some simple form validation. The <code>checkAge</code> library function checks ages to be within a reasonable range for humans and the <code>validateForm</code> function takes a field name and a checker function and checks the field’s value with the supplied checker.</p>
<p>The problem happens when <code>Main.coffee</code> sets up the form submit handler to call <code>validateForm</code> (which expects a checker that takes strings) with <code>checkAge</code> (which takes numbers).</p>
<p>Notice that the violation happens at <code>Validator.coffee:4</code> when the checker is called with a string but the module at fault is actually <code>Main.coffee</code> (since it was responsible for providing <code>Validator.coffee</code> with the right checker). In this case it is pretty much impossible to correctly assign blame by just inspecting the stacktrace since the the point of failure is in a different location than the file actually at fault.</p>
<p>But the error message gets it right!</p>
<p>It gets it right because <code>use()</code> records the module name where the contracted values were <code>used()</code> and we correctly track all the module names through all the subsequent contract checks.</p>
<span id='simple' />
<h2 id='simple_contracts'>Simple Contracts</h2>
<p>In addition to the <code>Num</code> contract that checks for numbers, we also have <code>Str</code>, <code>Bool</code>, <code>Null</code>, <code>Undefined</code>, <code>Nat</code>, <code>Pos</code>, <code>Neg</code>, <code>Any</code> (everything is ok), and <code>None</code> (nothing is ok).</p>
<span id='functions' />
<h2 id='functions'>Functions</h2>
<p>Basic functions:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>f</span> <span class='o'>::</span> <span class='nf'>(Num) -></span> <span class='nx'>Num</span>
<span class='nv'>f = </span><span class='nf'>(x) -></span> <span class='nx'>x</span>
</code></pre>
</div>
<p>Multiple arguments:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>f</span> <span class='o'>::</span> <span class='nf'>(Num, Str, Bool) -></span> <span class='nx'>Num</span>
<span class='nv'>f = </span><span class='nf'>(n, s, b) -></span> <span class='c1'># ...</span>
</code></pre>
</div>
<p>Optional arguments:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>f</span> <span class='o'>::</span> <span class='nf'>(Num, Str, Bool?) -></span> <span class='nx'>Num</span>
<span class='nv'>f = </span><span class='nf'>(n, s, b) -></span> <span class='c1'># ...</span>
</code></pre>
</div>
<p>All optional arguments must come at the end of the arguments list.</p>
<p>Higher order functions:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>f</span> <span class='o'>::</span> <span class='p'>(</span><span class='nf'>(Num) -></span> <span class='nx'>Bool</span><span class='p'>,</span> <span class='nx'>Num</span><span class='p'>)</span> <span class='o'>-></span> <span class='nx'>Bool</span>
<span class='nv'>f = </span><span class='nf'>(g, n) -></span> <span class='c1'># ...</span>
</code></pre>
</div>
<p>Functions that <em>cannot</em> be called with the <code>new</code> keyword:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>f</span> <span class='o'>::</span> <span class='p'>(</span><span class='nx'>Num</span><span class='p'>)</span> <span class='o'>--></span> <span class='nx'>Num</span>
<span class='nv'>f = </span><span class='nf'>(n) -></span> <span class='c1'># ...</span>
<span class='c1'>#...</span>
<span class='nv'>g = </span><span class='nx'>f</span> <span class='mi'>42</span> <span class='c1'># ok</span>
<span class='nv'>g = </span><span class='k'>new</span> <span class='nx'>f</span> <span class='mi'>42</span> <span class='c1'># error!</span>
</code></pre>
</div>
<p>Functions that can <em>only</em> be called with the <code>new</code> keyword:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>f</span> <span class='o'>::</span> <span class='p'>(</span><span class='nx'>Num</span><span class='p'>)</span> <span class='o'>==></span> <span class='nx'>Num</span>
<span class='nv'>f = </span><span class='nf'>(n) -></span> <span class='c1'># ...</span>
<span class='c1'>#...</span>
<span class='nv'>g = </span><span class='nx'>f</span> <span class='mi'>42</span> <span class='c1'># error!</span>
<span class='nv'>g = </span><span class='k'>new</span> <span class='nx'>f</span> <span class='mi'>42</span> <span class='c1'># ok</span>
</code></pre>
</div>
<p>Dependent functions:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>inc</span> <span class='o'>::</span> <span class='nf'>(Num) -></span> <span class='o'>!</span><span class='nf'>(result) -></span> <span class='nx'>result</span> <span class='o'>></span> <span class='nx'>$1</span>
<span class='nv'>inc = </span><span class='nf'>(n) -></span> <span class='nx'>n</span> <span class='o'>+</span> <span class='mi'>1</span>
</code></pre>
</div>
<p>The variable <code>$1</code> is the first argument passed to the function (<code>$2</code> would be the second argument, <code>$3</code> the third, and so on). This allows us to compare the result of the function to its arguments. Note that the test is run after the function has completed so if any of the arguments were mutated during the function’s execution the test could give spurious results.</p>
<p>The <code>this</code> contract:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>f</span> <span class='o'>::</span> <span class='nf'>(Str, @{name: Str}) -></span> <span class='nx'>Str</span>
<span class='nv'>f = </span><span class='nf'>(s) -></span> <span class='c1'>#...</span>
<span class='nv'>o = </span><span class='p'>{</span> <span class='nv'>name: </span><span class='s2'>"foo"</span><span class='p'>,</span> <span class='nv'>f: </span><span class='nx'>f</span><span class='p'>}</span>
<span class='nx'>o</span><span class='p'>.</span><span class='nx'>f</span><span class='p'>()</span>
</code></pre>
</div>
<p>Checks that <code>this</code> matches the given object contract.</p>
<span id='objects' />
<h2 id='objects'>Objects</h2>
<p>Simple properties:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>o</span> <span class='o'>::</span>
<span class='nv'>a: </span><span class='nx'>Str</span>
<span class='nv'>b: </span><span class='nx'>Num</span>
<span class='nv'>f: </span><span class='nf'>(Num) -></span> <span class='nx'>Num</span>
<span class='nv'>o =</span>
<span class='nv'>a: </span><span class='s2'>"foo"</span>
<span class='nv'>b: </span><span class='mi'>42</span>
<span class='nv'>f: </span><span class='nf'>(x) -></span> <span class='nx'>x</span>
</code></pre>
</div>
<p>Optional properties:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>o</span> <span class='o'>::</span>
<span class='nv'>a: </span><span class='nx'>Str</span>
<span class='nv'>b: </span><span class='nx'>Num</span><span class='o'>?</span>
<span class='nv'>f: </span><span class='nf'>(Num) -></span> <span class='nx'>Num</span>
<span class='nv'>o =</span>
<span class='nv'>a: </span><span class='s2'>"foo"</span>
<span class='nv'>f: </span><span class='nf'>(x) -></span> <span class='nx'>x</span>
</code></pre>
</div>
<p>Nested objects:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>o</span> <span class='o'>::</span>
<span class='nv'>oo: </span><span class='p'>{</span> <span class='nv'>a: </span><span class='nx'>Str</span> <span class='p'>}</span>
<span class='nv'>b: </span><span class='nx'>Num</span>
<span class='nv'>o =</span>
<span class='nv'>oo: </span><span class='p'>{</span> <span class='nv'>a: </span><span class='s2'>"foo"</span> <span class='p'>}</span>
<span class='nv'>b: </span><span class='mi'>42</span>
</code></pre>
</div>
<p>Recursive objects:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>o</span> <span class='o'>::</span>
<span class='nv'>a: </span><span class='nx'>Num</span>
<span class='nv'>b: </span><span class='nx'>Self</span>
<span class='nv'>c: </span><span class='nf'>(Num) -></span> <span class='nx'>Self</span>
<span class='nv'>inner: </span><span class='p'>{</span> <span class='nv'>y: </span><span class='nx'>Bool</span><span class='p'>,</span> <span class='nv'>z: </span><span class='nx'>Self</span> <span class='p'>}</span>
<span class='nv'>o = </span><span class='c1'>#...</span>
</code></pre>
</div>
<p><code>Self</code> binds to the closest object contract. So in this example, <code>Self</code> in <code>b</code> and <code>c</code> points to <code>o</code> and <code>Self</code> in <code>inner.z</code> points to <code>inner</code>.</p>
<p>Objects with functions that have pre and post conditions:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>o</span> <span class='o'>::</span>
<span class='nv'>a: </span><span class='nx'>Num</span>
<span class='nv'>f: </span><span class='nf'>(Num) -></span> <span class='nx'>Num</span> <span class='o'>|</span>
<span class='nv'>pre: </span><span class='nf'>(o) -></span> <span class='nx'>o</span><span class='p'>.</span><span class='nx'>a</span> <span class='o'>></span> <span class='mi'>10</span>
<span class='nv'>post: </span><span class='nf'>(o) -></span> <span class='nx'>o</span><span class='p'>.</span><span class='nx'>a</span> <span class='o'>></span> <span class='mi'>20</span>
<span class='nv'>o =</span>
<span class='nv'>a: </span><span class='mi'>12</span>
<span class='nv'>f: </span><span class='nf'>(x) -></span> <span class='err'>@</span><span class='p'>.</span><span class='nv'>a = </span><span class='err'>@</span><span class='p'>.</span><span class='nx'>a</span> <span class='o'>+</span> <span class='nx'>x</span>
</code></pre>
</div>
<p>The pre and post condition functions are called with the object that <code>f</code> is a member of. As their names imply, <code>pre</code> is called before the function <code>f</code> is invoked and <code>post</code> is called after.</p>
<p>Object invariants:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>o</span> <span class='o'>::</span>
<span class='nv'>a: </span><span class='nx'>Num</span>
<span class='nv'>f: </span><span class='nf'>(Num) -></span> <span class='nx'>Num</span> <span class='o'>|</span>
<span class='nv'>pre: </span><span class='nf'>(o) -></span> <span class='nx'>o</span><span class='p'>.</span><span class='nx'>a</span> <span class='o'>></span> <span class='mi'>10</span>
<span class='nv'>post: </span><span class='nf'>(o) -></span> <span class='nx'>o</span><span class='p'>.</span><span class='nx'>a</span> <span class='o'>></span> <span class='mi'>20</span>
<span class='o'>|</span> <span class='nv'>invariant: </span><span class='o'>-></span>
<span class='err'>@</span><span class='p'>.</span><span class='nx'>a</span> <span class='o'>></span> <span class='mi'>0</span> <span class='o'>and</span> <span class='err'>@</span><span class='p'>.</span><span class='nx'>a</span> <span class='o'><</span> <span class='mi'>100</span>
<span class='nv'>o =</span>
<span class='nv'>a: </span><span class='mi'>12</span>
<span class='nv'>f: </span><span class='nf'>(x) -></span> <span class='err'>@</span><span class='p'>.</span><span class='nv'>a = </span><span class='err'>@</span><span class='p'>.</span><span class='nx'>a</span> <span class='o'>+</span> <span class='nx'>x</span>
</code></pre>
</div>
<p>The invariant is checked whenever there is a possibility of <code>o</code> mutating (on property sets, delete, etc.).</p>
<span id='arrays' />
<h2 id='arrays'>Arrays</h2>
<p>Basic arrays:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>a</span> <span class='o'>::</span> <span class='p'>[</span><span class='nx'>Num</span><span class='p'>,</span> <span class='nx'>Str</span><span class='p'>,</span> <span class='p'>[</span><span class='nx'>Bool</span><span class='p'>,</span> <span class='nx'>Num</span><span class='p'>]]</span>
<span class='nv'>a = </span><span class='p'>[</span><span class='mi'>42</span><span class='p'>,</span> <span class='s2'>"foo"</span><span class='p'>,</span> <span class='p'>[</span><span class='kc'>true</span><span class='p'>,</span> <span class='mi'>24</span><span class='p'>]</span>
</code></pre>
</div>
<p>This says the array must have three elements, the first being a <code>Num</code>, the second being a <code>Str</code>, and the third being another array.</p>
<p>Multiple elements:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>a</span> <span class='o'>::</span> <span class='p'>[...</span><span class='nx'>Num</span><span class='p'>]</span>
<span class='nv'>a = </span><span class='p'>[</span><span class='mi'>42</span><span class='p'>,</span> <span class='mi'>24</span><span class='p'>,</span> <span class='mi'>432</span><span class='p'>,</span> <span class='mi'>854</span><span class='p'>,</span> <span class='mi'>21</span><span class='p'>]</span>
</code></pre>
</div>
<p>The <code>...</code> operator says that the array will only contain <code>Num</code>s.</p>
<p>Mixing arrays</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>a</span> <span class='o'>::</span> <span class='p'>[</span><span class='nx'>Bool</span><span class='p'>,</span> <span class='nx'>Str</span><span class='p'>,</span> <span class='p'>...</span><span class='nx'>Num</span><span class='p'>]</span>
<span class='nv'>a = </span><span class='p'>[</span><span class='kc'>false</span><span class='p'>,</span> <span class='s2'>"foo"</span><span class='p'>,</span> <span class='mi'>432</span><span class='p'>,</span> <span class='mi'>854</span><span class='p'>,</span> <span class='mi'>21</span><span class='p'>]</span>
</code></pre>
</div>
<p>The <code>...</code> operator can be mixed with single contracts. This says that the array’s first and second positions must pass the first two contracts and the remaining array positions must pass <code>Num</code>. The <code>...</code> operator must be in the last position of the array contract.</p>
<span id='operators' />
<h2 id='contract_operators'>Contract Operators</h2>
<p>The <code>or</code> contract:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>o</span> <span class='o'>::</span> <span class='p'>{</span> <span class='nv'>a: </span><span class='nx'>Num</span> <span class='o'>or</span> <span class='nx'>Str</span> <span class='p'>}</span>
<span class='nv'>o = </span><span class='p'>{</span> <span class='nv'>a: </span><span class='mi'>42</span> <span class='p'>}</span>
</code></pre>
</div>
<p>Here, the <code>a</code> property must pass either the <code>Num</code> or <code>Str</code> contract. Note that since contracts like function and object have deferred checking, they cannot be used with the <code>or</code> contract. Or to be more precise only <em>one</em> function/object contract can be used with <code>or</code>. So you could have <code>Num or (Num) -> Num</code> but not <code>((Num) -> Num) or ((Str) -> Str)</code>. If you combine normal contracts and function/object contracts with <code>or</code> all the normal contracts will be checked first and then the function/object contract will be applied.</p>
<p>The <code>and</code> contract:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>o</span> <span class='o'>::</span> <span class='p'>{</span> <span class='nv'>a: </span><span class='nx'>Num</span> <span class='o'>and</span> <span class='nx'>Even</span> <span class='p'>}</span>
<span class='nv'>o = </span><span class='p'>{</span> <span class='nv'>a: </span><span class='mi'>42</span> <span class='p'>}</span>
</code></pre>
</div>
<p>The <code>a</code> property must pass both the <code>Num</code> and <code>Even</code> contracts. Just like <code>or</code> you cannot use multiple function/object contracts with <code>and</code>.</p>
<span id='naming' />
<h2 id='naming_your_own_contracts'>Naming your own contracts</h2>
<p>You can bind a contract to a variable just like normal expressions:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nv'>NumId = </span><span class='o'>?</span><span class='nf'>(Num) -></span> <span class='nx'>Num</span>
<span class='nx'>f</span> <span class='o'>::</span> <span class='nx'>NumId</span>
<span class='nv'>f = </span><span class='nf'>(x) -></span> <span class='nx'>x</span>
</code></pre>
</div>
<p>The <code>?</code> operator allows you to escape out of the normal expression language and into the contract language.</p>
<p>You can also escape from the contract language to the normal expression language:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nx'>takesEvens</span> <span class='o'>::</span> <span class='p'>(</span><span class='o'>!</span><span class='nf'>(x) -></span> <span class='nx'>x</span> <span class='o'>%</span> <span class='mi'>2</span> <span class='o'>is</span> <span class='mi'>0</span><span class='p'>)</span> <span class='o'>-></span> <span class='nx'>Num</span>
<span class='nv'>takesEvens = </span><span class='nf'>(x) -></span> <span class='nx'>x</span>
</code></pre>
</div>
<p>The result of the expression in the <code>!</code> escape must be a function that returns a boolean. It is converted to a contract that checks its value against the function.</p>
<p>The standard <code>Num</code> and <code>Str</code> contracts you have seen are implemented as:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nv'>Num = </span><span class='o'>?!</span><span class='nf'>(x) -></span> <span class='k'>typeof</span> <span class='nx'>x</span> <span class='o'>is</span> <span class='s1'>'number'</span>
<span class='nv'>Str = </span><span class='o'>?!</span><span class='nf'>(x) -></span> <span class='k'>typeof</span> <span class='nx'>x</span> <span class='o'>is</span> <span class='s1'>'string'</span>
</code></pre>
</div>
<p>The syntax may look a little strange at first, but it can be read as “assign to <code>Num</code> the contract generated from the function <code>(x) ->
...</code></p>
<p>It could also be written:</p>
<div class='highlight'><pre><code class='coffeescript'><span class='nv'>Num = </span><span class='nf'>(x) -></span> <span class='k'>typeof</span> <span class='nx'>x</span> <span class='o'>is</span> <span class='s1'>'number'</span>
<span class='nv'>Str = </span><span class='nf'>(x) -></span> <span class='k'>typeof</span> <span class='nx'>x</span> <span class='o'>is</span> <span class='s1'>'string'</span>
<span class='nv'>f = </span><span class='nf'>(!Num) -></span> <span class='o'>!</span><span class='nx'>Str</span>
</code></pre>
</div>
<div class="footer">
get the source code on GitHub : <a href="http://github.com/disnet/contracts.coffee">disnet/contracts.coffee</a>
<div>
</div>
</div>
</div>
</div>
<script type="text/coffeescript">
compileSource = ->
source = $('#repl_source').val()
window.compiledJS = ''
try
window.compiledJS = CoffeeScript.compile source, { bare: on, contracts: on, contractPrefix: off}
el = $('#repl_results')[0]
if el.innerText
el.innerText = window.compiledJS
else
$(el).text window.compiledJS
$('#blame pre').text("")
catch error
$('#blame pre').text(error.message)
closeMenu = ->
$("#repl_container").removeClass "active"
$("#try_button").click ->
if $("#repl_container").hasClass('active')
closeMenu()
else
closeMenu()
$("#repl_container").addClass 'active'
false
# Eval the compiled js.
evalJS = ->
if typeof window.Proxy is 'undefined'
$('#blame pre').text "Your browser does not yet support Proxies which are required to run contracts.coffee.\nProxies currently work in Firefox 4+ with support for Chrome coming soon."
return false
try
eval window.compiledJS
catch error
$('#blame pre').text(error.message)
# Listen for keypresses and recompile.
$('#repl_source').keyup -> compileSource()
$('#run_button').click evalJS
# Dismiss console if Escape pressed or click falls outside console
# Trigger Run button on Ctrl-Enter
$(document.body)
.keydown (e) ->
closeMenu() if e.which == 27
evalJS() if e.which == 13 and (e.metaKey or e.ctrlKey) # and $('.minibutton.run:visible').length
.click (e) ->
return false if $(e.target).closest("#repl_container").length > 0
closeMenu()
$(document).ready ->
compileSource()
</script>
<script src="documentation/vendor/jquery-1.4.2.js"></script>
<script src="extras/coffee-script.js"></script>
<script src="lib/loadContracts.js"></script>
</body>
</html>