Make syntax somewhat less bizarre. Explain critical arguments.
catseye
12 years ago
16 | 16 | |
17 | 17 | * You can statically analyze the bastard, and prove that all of its |
18 | 18 | loops eventually terminate, and so forth; or |
19 | * You can write it in a language which naturally restricts you to the | |
20 | primitive recursive functions. | |
19 | * You can write it in a language which is inherently restricted to | |
20 | expressing only primitive recursive functions. | |
21 | 21 | |
22 | 22 | The second option is the route [PL-{GOTO}][] takes. But that's an imperative |
23 | 23 | language, and it's fairly easy to restrict an imperative language in this |
37 | 37 | primitive recursive functions? It *should* be possible... and easier than |
38 | 38 | statically analyzing an arbitrary program... but it's not immediately trivial. |
39 | 39 | Functional languages don't do the `for` loop thing, they do the recursion |
40 | thing, and there are no natural bound on recursion, and they would have | |
40 | thing, and there are no natural bounds on that recursion, so those would have | |
41 | 41 | to be embodied by the grammar, and... well, it sounds interesting, but |
42 | 42 | doable. So let's try it. |
43 | 43 | |
54 | 54 | |
55 | 55 | * It doesn't perform mutual recursion. |
56 | 56 | * When recursion happens, it's always with arguments that are strictly |
57 | "smaller" than the arguments the function received. | |
58 | * There is always a base case to the recursion, so that it always | |
57 | "smaller" values than the arguments the function received. | |
58 | * There is a "smallest" value that an argument can take on, so that | |
59 | there is always a base case to the recursion, so that it always | |
59 | 60 | eventually terminates. |
60 | 61 | * Higher-order functions are not used. |
61 | 62 | |
68 | 69 | "smallerness". (Gee, typing that made me feel a bit like George W. Bush!) |
69 | 70 | |
70 | 71 | The third point can be enforced by providing some default behaviour when |
71 | functions are called with the "smallest" kinds of values. | |
72 | functions are called with the "smallest" kinds of values. This could be | |
73 | as simple as terminating the program if you try to find a value "smaller" | |
74 | than the "smallest" value. | |
72 | 75 | |
73 | 76 | The fourth point can be enforced by simply disallowing functions to be |
74 | 77 | passed to, or returned from, functions. |
75 | 78 | |
76 | TODO explain critical arguments and how the only critical argument in | |
77 | an Exanoke function is always the first argument. | |
79 | ### Note on Critical Arguments ### | |
80 | ||
81 | I should note, though, that the second point is an oversimplification. | |
82 | Not *all* arguments need to be strictly "smaller" upon recursion -- only | |
83 | those arguments which are used to determine *if* the function recurses. | |
84 | I'll call those the _critical arguments_. Other arguments can take on | |
85 | any value (which is useful for having "accumulator" arguments and such.) | |
86 | ||
87 | When statically analyzing a function for primitive recursive-ness, you | |
88 | need to check how it decides to rescurse, to find out which arguments are | |
89 | the critical arguments, so you can check that those ones always get | |
90 | "smaller". | |
91 | ||
92 | But we can proceed in a simpler fashion here -- we can simply say that | |
93 | the first argument to every function is the critical argument, and all | |
94 | the rest aren't. I believe this is without loss of generality, as we can | |
95 | always split some functionality which would require more than one critical | |
96 | argument across multiple functions, each of which only has one critical | |
97 | argument. (Much like every `for` loop has only one loop variable.) | |
78 | 98 | |
79 | 99 | Data types |
80 | 100 | ---------- |
85 | 105 | the second element of each pair, and `NIL` is the agreed-upon list-terminating |
86 | 106 | atom, much love to it.) |
87 | 107 | |
88 | Arguments do not have user-defined names, they're just referred to strings of | |
89 | `#` symbols: `#` is the first argument to the function, `##` is the second, | |
90 | etc. When a function is defined, the name of the last argument is given, to | |
91 | specify how many arguments the function takes. | |
92 | ||
93 | 108 | Grammar |
94 | 109 | ------- |
95 | 110 | |
96 | 111 | Exanoke ::= {FunDef} Expr. |
97 | FunDef ::= "def" name<lowercase> "(" FirstArg | Arg ")" Expr. | |
98 | FirstArg ::= "#". | |
99 | Arg ::= "##" {"#"}. | |
100 | Expr ::= "cons" "(" Expr Expr ")" | |
112 | FunDef ::= "def" name<lowercase> "(" "#" {"," Arg} ")" Expr. | |
113 | Arg ::= name<lowercase>. | |
114 | Expr ::= "cons" "(" Expr "," Expr ")" | |
101 | 115 | | "if" Expr "then" Expr "else" Expr |
102 | | "self" "(" Smaller {Expr} ")" | |
103 | | "eq?" "(" Expr Expr")" | |
104 | | "cons?" Expr | |
105 | | "not" Expr | |
116 | | "self" "(" Smaller {"," Expr} ")" | |
117 | | "eq?" "(" Expr "," Expr")" | |
118 | | "cons?" "(" Expr ")" | |
119 | | "not" "(" Expr ")" | |
106 | 120 | | "(" Expr ")" |
107 | | name<lowercase> "(" {Expr} ")" | |
108 | | FirstArg | |
121 | | name<lowercase> "(" Expr {"," Expr} ")" | |
122 | | "#" | |
109 | 123 | | Arg |
110 | 124 | | Atom |
111 | 125 | | Smaller. |
116 | 130 | | FirstArg. |
117 | 131 | Atom ::= name<uppercase>. |
118 | 132 | |
119 | TODO still not entirely sure about `<if`. | |
133 | The first argument to a function does not have a user-defined name; it is | |
134 | simply referred to as `#`. | |
135 | ||
136 | The names of arguments defined in a function shall not shadow the names of | |
137 | any previously-defined functions. | |
138 | ||
139 | Note that `<if` does not seem to be truly necessary. Its only use is to embed | |
140 | a conditional into the first argument being passed to a recursive call. You | |
141 | could also use a regular `if` and make the recursive call in both branches, | |
142 | one with `TRUE` as the first argument and the other with `FALSE`. I think. | |
120 | 143 | |
121 | 144 | Examples |
122 | 145 | -------- |
123 | 146 | |
124 | | cons(HI THERE) | |
147 | | cons(HI, THERE) | |
125 | 148 | = (HI THERE) |
126 | 149 | |
127 | | (cons(HI cons(THERE NIL))) | |
150 | | (cons(HI, cons(THERE, NIL))) | |
128 | 151 | = (HI (THERE NIL)) |
129 | 152 | |
130 | | <head cons(HI THERE) | |
153 | | <head cons(HI, THERE) | |
131 | 154 | = HI |
132 | 155 | |
133 | | <tail cons(HI THERE) | |
156 | | <tail cons(HI, THERE) | |
134 | 157 | = THERE |
135 | 158 | |
136 | | <tail <tail (cons(HI cons(THERE NIL))) | |
159 | | <tail <tail (cons(HI, cons(THERE, NIL))) | |
137 | 160 | = NIL |
138 | 161 | |
139 | 162 | | <tail FOO |
140 | ? Not a cons cell | |
163 | ? tail: Not a cons cell | |
141 | 164 | |
142 | 165 | | <head BAR |
143 | ? Not a cons cell | |
166 | ? head: Not a cons cell | |
144 | 167 | |
145 | 168 | | if TRUE then HI else THERE |
146 | 169 | = HI |
148 | 171 | | if HI then HERE else THERE |
149 | 172 | = THERE |
150 | 173 | |
151 | | eq?(HI THERE) | |
174 | | eq?(HI, THERE) | |
152 | 175 | = FALSE |
153 | 176 | |
154 | | eq?(HI HI) | |
177 | | eq?(HI, HI) | |
155 | 178 | = TRUE |
156 | 179 | |
157 | | cons? HI | |
180 | | cons?(HI) | |
158 | 181 | = FALSE |
159 | 182 | |
160 | | cons? cons(WAGGA NIL) | |
183 | | cons?(cons(WAGGA, NIL)) | |
161 | 184 | = TRUE |
162 | 185 | |
163 | | not TRUE | |
186 | | not(TRUE) | |
164 | 187 | = FALSE |
165 | 188 | |
166 | | not FALSE | |
189 | | not(FALSE) | |
167 | 190 | = TRUE |
168 | 191 | |
169 | | not cons(WANGA NIL) | |
192 | | not(cons(WANGA, NIL)) | |
170 | 193 | = TRUE |
171 | 194 | |
172 | 195 | | # |
182 | 205 | |
183 | 206 | | def id(#) |
184 | 207 | | # |
185 | | id(FOO BAR) | |
208 | | id(FOO, BAR) | |
186 | 209 | ? Arity mismatch |
187 | 210 | |
188 | 211 | | def id(#) |
190 | 213 | | id(WOO) |
191 | 214 | ? Arity mismatch |
192 | 215 | |
193 | | def snd(##) | |
194 | | ## | |
195 | | snd(FOO BAR) | |
216 | | def snd(#, another) | |
217 | | another | |
218 | | snd(FOO, BAR) | |
196 | 219 | = BAR |
197 | 220 | |
198 | | def snd(##) | |
199 | | ## | |
221 | | def snd(#, another) | |
222 | | another | |
200 | 223 | | snd(FOO) |
201 | 224 | ? Arity mismatch |
202 | 225 | |
203 | | def snoc(##) | |
204 | | cons(## #) | |
226 | | def snoc(#, another) | |
227 | | cons(another, #) | |
205 | 228 | | snoc(THERE HI) |
206 | 229 | = (HI THERE) |
207 | 230 | |
208 | 231 | | def count(#) |
209 | 232 | | self(<tail #) |
210 | | count(cons(A cons(B NIL))) | |
211 | ? Not a cons cell | |
233 | | count(cons(A, cons(B, NIL))) | |
234 | ? tail: Not a cons cell | |
212 | 235 | |
213 | 236 | | def count(#) |
214 | | if eq?(# NIL) then NIL else self(<tail #) | |
215 | | count(cons(A cons(B NIL))) | |
237 | | if eq?(#, NIL) then NIL else self(<tail #) | |
238 | | count(cons(A, cons(B, NIL))) | |
216 | 239 | = NIL |
217 | 240 | |
218 | 241 | | def last(#) |
220 | 243 | | last(cons(A cons(B GRAAAP))) |
221 | 244 | = GRAAAP |
222 | 245 | |
223 | | def count(##) | |
224 | | if eq?(# NIL) then ## else self(<tail # cons(ONE ##)) | |
225 | | count(cons(A cons(B NIL)) NIL) | |
246 | | def count(#, acc) | |
247 | | if eq?(#, NIL) then ## else self(<tail #, cons(ONE, acc)) | |
248 | | count(cons(A, cons(B, NIL)), NIL) | |
226 | 249 | = (ONE (ONE NIL)) |
227 | 250 | |
228 | 251 | | def double(#) |
229 | | cons(# #) | |
252 | | cons(#, #) | |
230 | 253 | | def quadruple(#) |
231 | 254 | | double(double(#)) |
232 | | quadruple MEOW | |
255 | | quadruple(MEOW) | |
233 | 256 | = ((MEOW MEOW) (MEOW MEOW)) |
234 | 257 | |
235 | 258 | | def quadruple(#) |
236 | 259 | | double(double(#)) |
237 | 260 | | def double(#) |
238 | | cons(# #) | |
261 | | cons(#, #) | |
239 | 262 | | MEOW |
240 | 263 | ? Undefined function |
241 | 264 | |
242 | 265 | | def urff(#) |
243 | | self(cons(# #)) | |
266 | | self(cons(#, #)) | |
244 | 267 | | urff(WOOF) |
245 | 268 | ? Expected <smaller>, found "cons" |
246 | 269 | |
249 | 272 | | urff(GRAAAAP) |
250 | 273 | ? Expected <smaller>, found "#" |
251 | 274 | |
252 | | def urff(##) | |
253 | | self(##) | |
254 | | urff(GRAAAAP SKOOOORP) | |
255 | ? Expected <smaller>, found "##" | |
256 | ||
257 | | def urff(##) | |
258 | | self(<tail ##) | |
259 | | urff(GRAAAAP SKOOOORP) | |
260 | ? Expected <smallerterm>, found "##" | |
275 | | def urff(#, boof) | |
276 | | self(boof) | |
277 | | urff(GRAAAAP, SKOOOORP) | |
278 | ? Expected <smaller>, found "boof" | |
279 | ||
280 | | def urff(#, boof) | |
281 | | self(<tail boof) | |
282 | | urff(GRAAAAP, SKOOOORP) | |
283 | ? Expected <smallerterm>, found "boof" | |
261 | 284 | |
262 | 285 | | def urff(#) |
263 | 286 | | self(WANGA) |
265 | 288 | ? Expected <smaller>, found "WANGA" |
266 | 289 | |
267 | 290 | | def urff(#) |
268 | | self(if eq?(self(#) A) then <head # else <tail #) | |
269 | | urff(GRAAAAP) | |
270 | ? Expected <smaller>, found "self" | |
291 | | self(if eq?(A, A) then <head # else <tail #) | |
292 | | urff(GRAAAAP) | |
293 | ? Expected <smaller>, found "if" | |
294 | ||
295 | | def urff(#) | |
296 | | self(<if eq?(A, A) then <head # else <tail #) | |
297 | | urff(GRAAAAP) | |
298 | ? head: Not a cons cell | |
299 | ||
300 | | def urff(#) | |
301 | | self(<if eq?(self(<head #), A) then <head # else <tail #) | |
302 | | urff(GRAAAAP) | |
303 | ? head: Not a cons cell | |
271 | 304 | |
272 | 305 | | def urff(#) |
273 | 306 | | self(if self(<tail #) then <head # else <tail #) |
274 | 307 | | urff(cons(GRAAAAP FARRRRP)) |
275 | ? Not a cons cell | |
308 | ? tail: Not a cons cell | |
276 | 309 | |
277 | 310 | TODO more examples here... |
278 | 311 | |
279 | 312 | Discussion |
280 | 313 | ---------- |
281 | 314 | |
282 | I don't know if this holds water yet or not. | |
315 | I'm pretty sure this holds water, at this point. | |
283 | 316 | |
284 | 317 | The name "Exanoke" started life as a typo for the word "example". |