git @ Cat's Eye Technologies Exanoke / e256d8a
Update copyright date, license, README, and describe test cases. catseye 10 years ago
3 changed file(s) with 143 addition(s) and 45 deletion(s). Raw diff Collapse all Expand all
44
55 -----------------------------------------------------------------------------
66
7 Copyright (c)2012 Chris Pressey, Cat's Eye Technologies.
7 Copyright (c)2012, 2013 Chris Pressey, Cat's Eye Technologies.
88
99 The authors intend this Report to belong to the entire Exanoke
1010 community, and so we grant permission to copy and distribute it for
2020
2121 -----------------------------------------------------------------------------
2222
23 Copyright (c)2012 Chris Pressey, Cat's Eye Technologies.
23 Copyright (c)2012, 2013 Chris Pressey, Cat's Eye Technologies.
2424 All rights reserved.
2525
2626 Redistribution and use in source and binary forms, with or without
4949 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
5050 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
5151 POSSIBILITY OF SUCH DAMAGE.
52
53 -----------------------------------------------------------------------------
54
55 All example sources in the `eg` directory were written by Chris Pressey,
56 and are hereby placed in the public domain, as described in the following
57 unlicense:
58
59 -----------------------------------------------------------------------------
60
61 This is free and unencumbered software released into the public domain.
62
63 Anyone is free to copy, modify, publish, use, compile, sell, or
64 distribute this software, either in source code form or as a compiled
65 binary, for any purpose, commercial or non-commercial, and by any
66 means.
67
68 In jurisdictions that recognize copyright laws, the author or authors
69 of this software dedicate any and all copyright interest in the
70 software to the public domain. We make this dedication for the benefit
71 of the public at large and to the detriment of our heirs and
72 successors. We intend this dedication to be an overt act of
73 relinquishment in perpetuity of all present and future rights to this
74 software under copyright law.
75
76 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
77 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
78 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
79 IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
80 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
81 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
82 OTHER DEALINGS IN THE SOFTWARE.
83
84 For more information, please refer to <http://unlicense.org/>
44 expressing the primitive recursive functions.
55
66 I'll assume you know what a primitive recursive function is. If not, go look
7 it up, it's quite interesting, even if only for the fact that it demonstrates
8 even a genius like Kurt Goedel can sometimes be mistaken. (He initially
7 it up, as it's quite interesting, if only for the fact that it demonstrates
8 even a genius like Kurt Gödel can sometimes be mistaken. (He initially
99 thought that all functions could be expressed primitive recursively, until
1010 Ackermann came up with a counterexample.)
1111
2222 way. In PL-{GOTO}'s case, they just took PL and removed the `GOTO` construct.
2323 The rest of the language essentially contains only `for` loops, so what you
2424 get is something in which you can only express primitive recursive functions.
25 (That imperative programs consisting of only `for` loops can express only and
26 exactly the primitive recursive functions was established by Meyer and Ritchie
27 in "The complexity of loop programs".)
2528
2629 But what about functional languages?
2730
3235
3336 Thing is, that's kind of difficult. Is it possible to take the same approach
3437 PL-{GOTO} takes, and *syntactically* restrict a functional language to the
35 primitive recursive functions? It *should* be possible... and easier than
36 statically analyzing an arbitrary program... but it's not immediately trivial.
38 primitive recursive functions?
39
40 I mean, in a trivial sense, it must be; the original primitive recursive
41 formulae *were* functions. But they were highly arithmetical, with bounded
42 sums and products and whatnot. What would it look like in the setting of
43 general (and symbolic) functional programming?
44
3745 Functional languages don't do the `for` loop thing, they do the recursion
3846 thing, and there are no natural bounds on that recursion, so those would have
3947 to be embodied by the grammar, and... well, it sounds interesting, but
7785 ### Note on Critical Arguments ###
7886
7987 I should note, though, that the second point is an oversimplification.
80 Not *all* arguments need to be strictly "smaller" upon recursion -- only
88 Not *all* arguments need to be strictly "smaller" upon recursion — only
8189 those arguments which are used to determine *if* the function recurses.
8290 I'll call those the _critical arguments_. Other arguments can take on
8391 any value (which is useful for having "accumulator" arguments and such.)
8492
8593 When statically analyzing a function for primitive recursive-ness, you
86 need to check how it decides to rescurse, to find out which arguments are
94 need to check how it decides to recurse, to find out which arguments are
8795 the critical arguments, so you can check that those ones always get
8896 "smaller".
8997
90 But we can proceed in a simpler fashion here -- we can simply say that
98 But we can proceed in a simpler fashion here — we can simply say that
9199 the first argument to every function is the critical argument, and all
92 the rest aren't. I believe this is without loss of generality, as we can
93 always split some functionality which would require more than one critical
100 the rest aren't. This is without loss of generality, as we can always
101 split some functionality which would require more than one critical
94102 argument across multiple functions, each of which only has one critical
95103 argument. (Much like every `for` loop has only one loop variable.)
96104
102110 find this syntax somewhat obnoxious, it is less obnoxious than requiring that
103111 atoms are in ALL CAPS, which is what Exanoke originally had. In truth, there
104112 would be no real problem with allowing atoms, parameters, and function names
105 to all be arbitrarily alphanumeric, but it would require more static context
106 checking to sort them all out, and we're trying to be heavily syntactic here.
113 (and even `self`) to all be arbitrarily alphanumeric, but it would require
114 more static context checking to sort them all out, and we're trying to be
115 as syntactic as reasonably possible here.
107116
108117 `:true` is the only truthy atom. Lists are by convention only, and, by
109118 convention, lists compose via the second element of each pair, and `:nil` is
138147 to allow the programmer to give it a better name, but more static context
139148 checking would be involved.
140149
141 Note that `<if` does not seem to be truly necessary. Its only use is to embed
142 a conditional into the first argument being passed to a recursive call. You
150 Note that `<if` is not truly necessary. Its only use is to embed a
151 conditional into the first argument being passed to a recursive call. You
143152 could also use a regular `if` and make the recursive call in both branches,
144153 one with `:true` as the first argument and the other with `:false`.
145154
151160 -> Functionality "Evaluate Exanoke program" is implemented by
152161 -> shell command "script/exanoke %(test-file)"
153162
154 Basic examples.
163 `cons` can be used to make lists and trees and things.
155164
156165 | cons(:hi, :there)
157166 = (:hi :there)
159168 | cons(:hi, cons(:there, :nil))
160169 = (:hi (:there :nil))
161170
171 `head` extracts the first element of a cons cell.
172
162173 | head(cons(:hi, :there))
163174 = :hi
164175
176 | head(:bar)
177 ? head: Not a cons cell
178
179 `tail` extracts the second element of a cons cell.
180
165181 | tail(cons(:hi, :there))
166182 = :there
167183
171187 | tail(:foo)
172188 ? tail: Not a cons cell
173189
174 | head(:bar)
175 ? head: Not a cons cell
190 `<head` and `<tail` and syntactic variants of `head` and `tail` which
191 expect their argument to be "smaller than or equal in size to" a critical
192 argument.
176193
177194 | <head cons(:hi, :there)
178195 ? Expected <smaller>, found "cons"
180197 | <tail :hi
181198 ? Expected <smaller>, found ":hi"
182199
200 `if` is used for descision-making.
201
183202 | if :true then :hi else :there
184203 = :hi
185204
186205 | if :hi then :here else :there
187206 = :there
188207
208 `eq?` is used to compare atoms.
209
189210 | eq?(:hi, :there)
190211 = :false
191212
197218 | eq?(cons(:one, :nil), cons(:one, :nil))
198219 = :false
199220
221 `cons?` is used to detect cons cells.
222
200223 | cons?(:hi)
201224 = :false
202225
203226 | cons?(cons(:wagga, :nil))
204227 = :true
205228
229 `not` does the expected thing when regarding atoms as booleans.
230
206231 | not(:true)
207232 = :false
208233
221246
222247 | self(:foo)
223248 ? Use of "self" outside of a function body
249
250 We can define functions. Here's the identity function.
224251
225252 | def id(#)
226253 | #
227254 | id(:woo)
228255 = :woo
229256
257 Functions must be called with the appropriate arity.
258
230259 | def id(#)
231260 | #
232261 | id(:foo, :bar)
233262 ? Arity mismatch (expected 1, got 2)
234263
264 | def snd(#, another)
265 | another
266 | snd(:foo)
267 ? Arity mismatch (expected 2, got 1)
268
269 Parameter names must be defined in the function definition.
270
235271 | def id(#)
236272 | woo
237273 | id(:woo)
238274 ? Undefined argument "woo"
239275
276 You can't call a parameter as if it were a function.
277
240278 | def wat(#, woo)
241279 | woo(#)
242280 | wat(:woo)
243281 ? Undefined function "woo"
282
283 You can't define two functions with the same name.
244284
245285 | def wat(#)
246286 | :there
249289 | wat(:woo)
250290 ? Function "wat" already defined
251291
292 You can't name a function with an atom.
293
252294 | def :wat(#)
253295 | #
254296 | :wat(:woo)
255297 ? Expected identifier, but found atom (':wat')
256298
299 Every function takes at least one argument.
300
301 | def wat()
302 | :meow
303 | wat()
304 ? Expected '#', but found ')'
305
306 The first argument of a function must be `#`.
307
257308 | def wat(meow)
258309 | meow
259310 | wat(:woo)
260311 ? Expected '#', but found 'meow'
261312
313 The subsequent arguments don't have to be called `#`, and in fact, they
314 shouldn't be.
315
262316 | def snd(#, another)
263317 | another
264318 | snd(:foo, :bar)
265319 = :bar
266320
267 | def snd(#, another)
268 | another
269 | snd(:foo)
270 ? Arity mismatch (expected 2, got 1)
321 | def snd(#, #)
322 | #
323 | snd(:foo, :bar)
324 ? Expected identifier, but found goose egg ('#')
325
326 A function can call a built-in.
271327
272328 | def snoc(#, another)
273329 | cons(another, #)
274330 | snoc(:there, :hi)
275331 = (:hi :there)
276
277 | def count(#)
278 | self(<tail #)
279 | count(cons(:alpha, cons(:beta, :nil)))
280 ? tail: Not a cons cell
281
282 | def count(#)
283 | if eq?(#, :nil) then :nil else self(<tail #)
284 | count(cons(:alpha, cons(:beta, :nil)))
285 = :nil
286
287 | def last(#)
288 | if not(cons?(#)) then # else self(<tail #)
289 | last(cons(:alpha, cons(:beta, :graaap)))
290 = :graaap
291
292 | def count(#, acc)
293 | if eq?(#, :nil) then acc else self(<tail #, cons(:one, acc))
294 | count(cons(:A, cons(:B, :nil)), :nil)
295 = (:one (:one :nil))
296332
297333 Functions can call other user-defined functions.
298334
322358 | snocsnoc(:blarch, :glamch)
323359 = (:blarch (:blarch :glamch))
324360
361 A function may recursively call itself, as long as it does so with
362 values which are smaller than or equal in size to the critical argument
363 as the first argument.
364
365 | def count(#)
366 | self(<tail #)
367 | count(cons(:alpha, cons(:beta, :nil)))
368 ? tail: Not a cons cell
369
370 | def count(#)
371 | if eq?(#, :nil) then :nil else self(<tail #)
372 | count(cons(:alpha, cons(:beta, :nil)))
373 = :nil
374
375 | def last(#)
376 | if not(cons?(#)) then # else self(<tail #)
377 | last(cons(:alpha, cons(:beta, :graaap)))
378 = :graaap
379
380 | def count(#, acc)
381 | if eq?(#, :nil) then acc else self(<tail #, cons(:one, acc))
382 | count(cons(:A, cons(:B, :nil)), :nil)
383 = (:one (:one :nil))
384
385 Arity must match when a function calls itself recursively.
386
325387 | def urff(#)
326388 | self(<tail #, <head #)
327389 | urff(:woof)
332394 | urff(:woof, :moo)
333395 ? Arity mismatch on self (expected 2, got 1)
334396
397 The remaining tests demonstrate that a function cannot call itself if it
398 does not pass a values which is smaller than or equal in size to the
399 critical argument as the first argument.
400
335401 | def urff(#)
336402 | self(cons(#, #))
337403 | urff(:woof)
377443 | urff(cons(:graaap, :skooorp))
378444 ? tail: Not a cons cell
379445
380 Some practical examples, on Peano naturals. Addition:
446 Now, some practical examples, on Peano naturals. Addition:
381447
382448 | def inc(#)
383449 | cons(:one, #)
33 if eq?(#, :nil) then other else self(<tail #, inc(other))
44 def mul(#, other)
55 if eq?(#, :nil) then :nil else
6 if eq?(#, cons(:one, :nil)) then other else
7 add(other, self(<tail #, other))
6 add(other, self(<tail #, other))
87 def fact(#)
98 if eq?(#, :nil) then cons(:one, :nil) else
109 mul(#, self(<tail #))