Mercurial is no longer supported here. Rename `*.markdown` to `*.md`.
Chris Pressey
1 year, 5 months ago
0 | a755dc2d82a3e7e51bf27f142d0f90d20fe0ea12 rel_1_0_2012_0316 | |
1 | fccf574a353121cc19650c06bac75d81bcf05dec rel_1_0_2013_1014 | |
2 | d1e706f743d97cc00e2f150ce2bae32a531f1c70 rel_1_0_2014_0819 |
0 | The Unlikely Programming Language | |
1 | ================================= | |
2 | ||
3 | Overview | |
4 | -------- | |
5 | ||
6 | Unlikely is a programming language with the following traits: | |
7 | ||
8 | - Unlikely conflates objects with continuations, and methods with | |
9 | labels. All classes are subclasses of the root class `Continuation`. | |
10 | Every object can be continued at any method. Every method, except | |
11 | for the `continue` method on the built-in class `Stop`, must | |
12 | continue some other method at the end of its definition. | |
13 | - All Unlikely program structures are exposed as objects with | |
14 | commensurate inheritance relationships. For example, every Unlikely | |
15 | program is a subclass of `Program`, and the classes `If` and | |
16 | `Switch` are both subclasses of the abstract base class `Branch`. | |
17 | - Unlikely takes dependency injection to the logical extreme. | |
18 | Dependency injection is an increasingly popular modern | |
19 | object-oriented program construction technique which allows the | |
20 | specific classes which will be used by some class ("dependencies") | |
21 | to be specified ("injected") when that class is instantiated. | |
22 | Unlikely goes one step further by *requiring* that all specific | |
23 | classes used by some class are specified during instantiation. | |
24 | ||
25 | Semantics | |
26 | --------- | |
27 | ||
28 | ### Classes | |
29 | ||
30 | A class is a schema describing a set of objects. Instantiating a class | |
31 | produces a new object of that class. When a class is instantiated, all | |
32 | classes that are referenced by the class (dependant classes) must be | |
33 | named (injected) by the instantiating code (the instantiator). For each | |
34 | requested dependant class, any subclass of it may be supplied by the | |
35 | instantiator, further specifying and constraining behaviour (a technique | |
36 | called dependency injection). In this way, classes are inherently | |
37 | parameterized. | |
38 | ||
39 | When a class refers to itself, it is considered a dependant class of | |
40 | itself; it (or a subclass) must be injected by the instantiator. | |
41 | ||
42 | All specified dependant classes must be unique (no dependant class may | |
43 | be specified more than once.) Final classes need not and may not be | |
44 | specified as dependant classes because, being final, there is no | |
45 | subclass that the instantiator could possibly substitute for them. | |
46 | ||
47 | Each class may define zero or more properties and zero or more methods. | |
48 | ||
49 | #### Inheritance | |
50 | ||
51 | Whenever a class is defined in Unlikely source code, a superclass must | |
52 | be named; the class being defined is thus a subclass that inherits from | |
53 | that superclass. Its inheritance consists of the properties and methods | |
54 | defined by the superclass and all of its ancestors (i.e. including all | |
55 | properties and methods that the superclass inherited from its own | |
56 | superclasses) as well as the dependant classes of the superclass. The | |
57 | subclass may not inject dependencies when inheriting from a superclass. | |
58 | Only single inheritance is supported. | |
59 | ||
60 | A subclass may override methods that it inherits from its superclass. It | |
61 | may access the method definition of its direct superclass (or any | |
62 | indirect ancestor class) by naming that superclass explicitly in a | |
63 | continue. | |
64 | ||
65 | A class may be declared as final, in which case it may not be | |
66 | subclassed. In addition, final dependant classes may not be injected. | |
67 | ||
68 | A class may also be declared as saturated, in which case it can be | |
69 | subclassed, but subclasses of it must also be declared as saturated, and | |
70 | they cannot define any new methods. They can only override existing | |
71 | methods. In fact, the root class `Continuation` is declared saturated, | |
72 | so really, all objects have exactly one method, `continue`. | |
73 | ||
74 | If a class defines or inherits any abstract methods, that class must be | |
75 | declared as abstract. Abstract classes cannot be instantiated. Any | |
76 | subclass of an abstract class must define all inherited abstract methods | |
77 | in order to be considered concrete and thus instantiatable. | |
78 | ||
79 | ### Properties | |
80 | ||
81 | Each property has a particular type, which is a class. The values it may | |
82 | take on are objects of that class, or of its subclasses. | |
83 | ||
84 | Each object has its own instances of the properties defined on the | |
85 | class, and each of these properties may take on different values. | |
86 | ||
87 | All state of an object is stored in its properties. Properties are | |
88 | effectively public; they can be modified by code in any method in any | |
89 | class. | |
90 | ||
91 | The root class `Continuation` defines one property, `accumulator`, of | |
92 | type `Passive`, which all classes inherit. | |
93 | ||
94 | Subclasses may not override inherited properties. | |
95 | ||
96 | ### Methods | |
97 | ||
98 | A method is a label on a piece of code inside a class. The methods of a | |
99 | class are shared by all objects of that class. | |
100 | ||
101 | A method may be declared abstract instead of defining code for it. | |
102 | Classes which contain abstract methods must themselves be declared | |
103 | abstract. | |
104 | ||
105 | Only one thing may be done to a method in code, which is to continue it | |
106 | with respect to some object; this is described in the next section. | |
107 | ||
108 | A method may declare zero or more arguments. Each argument has a type, | |
109 | which is a class. When a method is continued, a value of the | |
110 | corresponding type (or some subclass of that type) must be given for | |
111 | each argument. In actuality, the arguments merely name properties of the | |
112 | object; they must already be declared in the class before they are | |
113 | listed in the declaration of the method. Passing values in arguments is | |
114 | just shorthand for assigning these properties before continuing the | |
115 | method. | |
116 | ||
117 | Methods do not have local variables, so for storage must use the | |
118 | properties of the object on which they were continued. | |
119 | ||
120 | The root class `Continuation` defines one abstract method which | |
121 | subclasses must implement, called `continue(Passive accumulator)`. By | |
122 | convention, this method is what other methods continue. The accumulator | |
123 | is passed from continuation to continuation, manipulated as execution | |
124 | proceeds. | |
125 | ||
126 | ### Code | |
127 | ||
128 | The code inside a class labelled by a method consists of a series of | |
129 | assignments followed by a continue. | |
130 | ||
131 | Each assignment consists of an object property on the left-hand side, | |
132 | and an expression on the right-hand side. The the property so named will | |
133 | take on the value resulting from evalulating the given expression. | |
134 | Expressions consist of instantiations of new objects, and references to | |
135 | other object properties. | |
136 | ||
137 | The continue names a method on an object to which control flow will | |
138 | immediately pass. It may also pass values as arguments to that method; | |
139 | however, this is mere shorthand for assigning properties of the object | |
140 | on which the method being continued is defined. See above under | |
141 | "Methods" for more details. | |
142 | ||
143 | ### Passive Data | |
144 | ||
145 | Passive data values, such as integers and strings, are modelled somewhat | |
146 | specially in Unlikely, in order to strike a balance in language design | |
147 | between "too straightforward" and "too convoluted". | |
148 | ||
149 | All passive data values are instances of some subclass of the abstract | |
150 | class `Passive`, which is itself a subclass of `Chain`. When a passive | |
151 | data value is continued, it passes its own value into the accumulator of | |
152 | the "next" continuation. (It is not necessary, however, to continue the | |
153 | passive data value to obtain its value in all cases.) | |
154 | ||
155 | Each immediate subclass of `Passive` gives a data type of values, such | |
156 | as `Integer` or `String`. Each of these type-classes has a countably | |
157 | infinite number of subclasses, one for each possible value of that type. | |
158 | It is these classes that are instantiated to acquire a passive value | |
159 | object. For example, `three = new 3()` instantiates the value 3. When | |
160 | the `continue` method on this object is continued, the value that it | |
161 | represents will be passed down the chain to the continuation assigned to | |
162 | its `next` property. For example, `three.next = new Print()` would cause | |
163 | 3 to be printed when `three` was continued. | |
164 | ||
165 | None of the direct subclasses of `Passive` can be further subclassed. In | |
166 | effect, they are final (despite being abstract!) because all possible | |
167 | subclasses of them already exist. | |
168 | ||
169 | Syntax | |
170 | ------ | |
171 | ||
172 | ### Overview | |
173 | ||
174 | The overall schema of a class definition is: | |
175 | ||
176 | class ClassName(ClassName,ClassName) extends ClassName { | |
177 | ClassName propname; | |
178 | method methodname(ClassName propname, ClassName argname) { | |
179 | propname = new ClassName(ClassName,ClassName); | |
180 | propname.propertyname = argname; | |
181 | goto expr.methodname(expr,expr); | |
182 | } | |
183 | } | |
184 | ||
185 | ### Grammar | |
186 | ||
187 | A somewhat more formal definition of the syntactic structure of Unlikely | |
188 | code is given in the following EBNF-like grammar. | |
189 | ||
190 | ClassBase ::= {ClassDefn}. | |
191 | ClassDefn ::= "class" ClassName<NEW> "(" [ClassName {"," ClassName}] ")" "extends" ClassName | |
192 | ["{" {PropDefn} {MethodDefn} "}"] ["is" ClassMod {"and" ClassMod}]. | |
193 | ClassMod ::= "final" | "saturated" | "abstract". | |
194 | PropDefn ::= ClassName PropName<NEW> ";". | |
195 | MethodDefn ::= "method" MethodName<NEW> "(" [ParamDecl {"," ParamDecl}] ")" | |
196 | ("{" {Assignment} Continue "}" | "is" "abstract"). | |
197 | ParamDecl ::= ClassName PropName. | |
198 | Assignment ::= QualName "=" Expr ";". | |
199 | Continue ::= "goto" PropName "." MethodName "(" [Expr {"," Expr}] ")" ";". | |
200 | Expr ::= ConstrExpr | QualName. | |
201 | ConstrExpr ::= "new" (ClassName | Constant) "(" [ClassName {"," ClassName}] ")". | |
202 | QualName ::= PropName {"." PropName}. | |
203 | ClassName ::= <<sequence of alphabetic characters>> | Constant. | |
204 | Constant ::= <<sequence of decimal digits>> | <<sequence of arbitrary characters between double quotes>>. | |
205 | ||
206 | Note that multiple ClassDefns for a single class may appear; each may | |
207 | partially define the class. In this way a "forward declaration" of a | |
208 | class may occur. This may give its name, superclass, and dependant | |
209 | classes, so these can be consumed by some following class that requires | |
210 | them, before the methods of this class are defined. The dependant | |
211 | classes are cumulative over successive partial definitions; they need | |
212 | not be repeated. However the same superclass must be specified for all | |
213 | partial definitions of a class. | |
214 | ||
215 | Built-in Classes | |
216 | ---------------- | |
217 | ||
218 | - `Continuation` | |
219 | ||
220 | The abstract base class that is the superclass of all Unlikely | |
221 | classes, and which can't be instantiated. Declares the property | |
222 | `Passive accumulator`, and the abstract method | |
223 | `continue(Passive accumulator)`, which all concrete subclasses must | |
224 | implement. Is declared `saturated`, so no subclass may declare or | |
225 | define any additional methods. | |
226 | ||
227 | - `Program` | |
228 | ||
229 | An abstract continuation that provides the guarantee that it can | |
230 | be started (that is, that its `continue` method can be initially | |
231 | continued) from the operating system. It can be reasonably | |
232 | expected that the `accumulator` will be assigned a value | |
233 | provided by the user, perhaps via a command-line argument. | |
234 | ||
235 | - `Stop` | |
236 | ||
237 | A concrete continuation which exits to the operating system | |
238 | when continued. The accumulator is used as the exit code, | |
239 | assuming the operating system supports this. | |
240 | ||
241 | - `Chain` | |
242 | ||
243 | An abstract continuation which defines the property | |
244 | `Continuation next`. When a subclass of `Chain` is | |
245 | continued, it will generally continue the continuation | |
246 | assigned to its `next` property after doing something. | |
247 | ||
248 | - `Passive` | |
249 | ||
250 | The abstract final base class representing passive data | |
251 | values. Discards whatever accumulator was passed to it, | |
252 | and passes its own value into the accumulator when it | |
253 | continues the continuation assigned to its `next` | |
254 | property. | |
255 | ||
256 | - `Boolean` | |
257 | ||
258 | The abstract final base class representing boolean | |
259 | values. | |
260 | ||
261 | - `True`, `False` | |
262 | ||
263 | Final classes representing particular boolean | |
264 | values. | |
265 | ||
266 | - `Integer` | |
267 | ||
268 | The abstract final base class representing integer | |
269 | values. | |
270 | ||
271 | - `0`, `1`, `2`... | |
272 | ||
273 | Final classes representing particular integer | |
274 | values. Note that only positive constants are | |
275 | available; negative values must be computed by | |
276 | subtracting from 0. | |
277 | ||
278 | - `String` | |
279 | ||
280 | The abstract final class representing string values. | |
281 | ||
282 | - `""`, `"a"`, `"b"`, `"aa"`... | |
283 | ||
284 | Final classes representing particular string | |
285 | values. | |
286 | ||
287 | - `BinaryOperation` | |
288 | ||
289 | A continuation which posesses a property `value` and | |
290 | which yields a value when continued by applying some | |
291 | operation to `value` (treated as LHS) and the | |
292 | accumulator (treated as RHS.) | |
293 | ||
294 | - `Add` | |
295 | ||
296 | A concrete binary operation which adds `value` to | |
297 | the accumulator and passes the result when | |
298 | continuing `next`. | |
299 | ||
300 | - `Subtract` | |
301 | ||
302 | A concrete binary operation which subtracts the | |
303 | accumulator from `value` and passes the result when | |
304 | continuing `next`. | |
305 | ||
306 | - `Multiply` | |
307 | ||
308 | A concrete binary operation which multiplies `value` | |
309 | by the accumulator and passes the result when | |
310 | continuing `next`. | |
311 | ||
312 | - `Divide` | |
313 | ||
314 | A concrete binary operation which divides `value` by | |
315 | the accumulator and passes the result when | |
316 | continuing `next`. | |
317 | ||
318 | - `Condition` | |
319 | ||
320 | A BinaryOperation which yields a boolean value. | |
321 | ||
322 | - `Equal` | |
323 | ||
324 | A concrete binary operation which compares | |
325 | `value` to the accumulator and passes True when | |
326 | continuing `next` only if they are equal. | |
327 | ||
328 | - `GreaterThan` | |
329 | ||
330 | A concrete binary operation which compares | |
331 | `value` to the accumulator and passes True when | |
332 | continuing `next` only if `value` is greater. | |
333 | ||
334 | - `Print` | |
335 | ||
336 | A concrete continuation which displays the value of the | |
337 | accumulator to the user before continuing its `next` | |
338 | property. | |
339 | ||
340 | - `Input` | |
341 | ||
342 | A concrete continuation which obtains a value from the | |
343 | user, and passes this value in the accumulator when | |
344 | continuing its `next` property. | |
345 | ||
346 | - `Branch` | |
347 | ||
348 | An abstract continuation which continues one of the | |
349 | continuations assigned to its properties, based on some | |
350 | other information. Defines the `Chain` property `else` | |
351 | which represents the basic alternate continuation that | |
352 | can be continued instead of `next`. | |
353 | ||
354 | - `If` | |
355 | ||
356 | A continuation which continues one of two | |
357 | continuations assigned to its properties, `next` and | |
358 | `else`, based on whether the accumulator is an | |
359 | instance of `True` or `False`. | |
360 | ||
361 | - `Switch` | |
362 | ||
363 | An abstract continuation whose behaviour differs on | |
364 | subsequent times it is continued. This is | |
365 | implemented by means of a `state` property which | |
366 | changes values each time the switch is continued; | |
367 | depending on the value of `state`, different things | |
368 | happen. | |
369 | ||
370 | - `Loop` | |
371 | ||
372 | An abstract continuation which is intended to | |
373 | implement a loop. However, this is not | |
374 | automatic; some continuation chain leading from | |
375 | this continuation *must* lead back to this | |
376 | continuation in order for actual repetition to | |
377 | take place. | |
378 | ||
379 | - `WhileLoop` | |
380 | ||
381 | A concrete `Loop` which, on odd visits | |
382 | continues its `test` property. It is assumed | |
383 | that some continuation down that chain | |
384 | continues this `WhileLoop` object with a | |
385 | boolean value in the accumulator. On these | |
386 | even visits, it will behave like an `If`: | |
387 | when the boolean is a `True`, `next` is | |
388 | continued, otherwise `else`. | |
389 | ||
390 | - `ForLoop` | |
391 | ||
392 | A concrete `Loop` which defines `value`, | |
393 | `delta`, and `finish` properties, all | |
394 | `Integer`s. On each visit, it checks if | |
395 | `value` equals `finish`; if not, it adds | |
396 | `delta` to `value` and continues `next`. But | |
397 | if so, it simply continues `else`. | |
398 | ||
399 | Implementations | |
400 | --------------- | |
401 | ||
402 | There is not currently a reference implementation of Unlikely. Any | |
403 | contradictions and/or grey areas in this document will therefore have | |
404 | nowhere to turn to to be cleared up. | |
405 | ||
406 | There is, however, a partial and non-normative implementation of | |
407 | Unlikely, a static analyzer written in Python called Coldwater. It | |
408 | parses Unlikely programs and identifies many type errors and other | |
409 | statically-detectable invalid programs. It is meant to give some body to | |
410 | the ideas present in Unlikely, without actually going so far as | |
411 | implementing it in full. | |
412 | ||
413 | The construction of Coldwater helped clear up some of the fuzzier | |
414 | corners of the language. However, there are probably several areas that | |
415 | remain fuzzy and render the language unsuitable for anything but the | |
416 | most trivial programming. Extending Coldwater to be a full-fledged | |
417 | Unlikely interpreter will probably help define the language. However | |
418 | that project has been deferred as future work, and any clarifications | |
419 | that come from it will be incorporated only in a future language | |
420 | version. | |
421 | ||
422 | Discussion | |
423 | ---------- | |
424 | ||
425 | This section contains some random thoughts and reflections on the | |
426 | Unlikely programming language. | |
427 | ||
428 | There is a rough analogy between Unlikely's requisite dependency | |
429 | injection class parameters, and value parameters in languages without | |
430 | global variables. There is no implicit referent when you say `Foo`; | |
431 | `Foo` must name some parameter that has been passed into scope. | |
432 | ||
433 | Because all the parts of the language are modelled as objects, the | |
434 | language's execution model has some resemblance to an object-oriented | |
435 | AST interpreter for itself. Except, of course, these objects are | |
436 | continuations, so it is not like a recursive, depth-first walk of the | |
437 | AST; it is much closer to the technique of threading the AST and | |
438 | following that thread. | |
439 | ||
440 | At one point I included the constraint that the set of dependant classes | |
441 | specified by a class must be mutually disjoint; that is, no dependant | |
442 | class in the set could be a subclass of some other dependant class in | |
443 | the set. I am not entirely sure why I introduced that constraint, since | |
444 | it could well be valuable to refine two classes by injection even when | |
445 | one of those classes is a subclass of the other. I took it out. | |
446 | ||
447 | Because properties cannot be redefined in subclasses, and because | |
448 | parameters to methods are just syntactic sugar for properties, methods | |
449 | cannot be overloaded. In particular `continue` must always work on a | |
450 | `Passive` parameter, although of course it is mere convention that the | |
451 | method works on the `accumulator` property anyway. | |
452 | ||
453 | Unlikely is Turing-complete. (I will assert this because it seems | |
454 | reasonable to me, but I do not have a proof, so I may be wrong.) The | |
455 | Unlikely language is not minimal. In particular, the `Loop` class and | |
456 | its subclasses `WhileLoop` and `ForLoop` could be removed, and the | |
457 | resulting language would still be Turing-complete. In fact, `WhileLoop` | |
458 | and `ForLoop` could probably be implemented in Unlikely and provided in | |
459 | an extension class library. | |
460 | ||
461 | The idea to conflate contintuations and objects was inspired by the | |
462 | data-structure representation of continuations in Chapter 7 of | |
463 | [Essentials of Programming Languages, 2nd | |
464 | ed.](http://www.cs.indiana.edu/eopl/), which embodies a tiny measure of | |
465 | inheritance. The idea that language constructs have commensurate | |
466 | inheritance relationships (`WhileLoop` and `ForLoop` are subclasses of | |
467 | `Loop`, etc.) was borrowed from Steve Yegge's article [Scheming is | |
468 | Believing](http://steve.yegge.googlepages.com/scheming-is-believing). | |
469 | The idea that all programs are subclasses of `Program`, which dovetails | |
470 | so nicely with that, was borrowed from Brian Connors' "Sulawesi" | |
471 | language design. The idea that every concrete value has its own class, | |
472 | leading to abstract final classes with countably infinite subclasses, | |
473 | was pure desperation. | |
474 | ||
475 | For the purpose of defining computable functions, the Unlikely-Calculus | |
476 | could be considered as a variant of Unlikely without `Print` or `Input` | |
477 | classes. The `Stop` class would be there redefined to yield the value | |
478 | passed to the accumulator as the result of evaluation, rather than as an | |
479 | operating system exit code. | |
480 | ||
481 | It's a safe bet that there is at least one person out there who will be | |
482 | disappointed that this language is named Unlikely yet contains no | |
483 | probabilistic design features. | |
484 | ||
485 | Happy object-method-continuing! | |
486 | Chris Pressey | |
487 | March 15, 2009 | |
488 | Bellevue, WA |
0 | The Unlikely Programming Language | |
1 | ================================= | |
2 | ||
3 | Overview | |
4 | -------- | |
5 | ||
6 | Unlikely is a programming language with the following traits: | |
7 | ||
8 | - Unlikely conflates objects with continuations, and methods with | |
9 | labels. All classes are subclasses of the root class `Continuation`. | |
10 | Every object can be continued at any method. Every method, except | |
11 | for the `continue` method on the built-in class `Stop`, must | |
12 | continue some other method at the end of its definition. | |
13 | - All Unlikely program structures are exposed as objects with | |
14 | commensurate inheritance relationships. For example, every Unlikely | |
15 | program is a subclass of `Program`, and the classes `If` and | |
16 | `Switch` are both subclasses of the abstract base class `Branch`. | |
17 | - Unlikely takes dependency injection to the logical extreme. | |
18 | Dependency injection is an increasingly popular modern | |
19 | object-oriented program construction technique which allows the | |
20 | specific classes which will be used by some class ("dependencies") | |
21 | to be specified ("injected") when that class is instantiated. | |
22 | Unlikely goes one step further by *requiring* that all specific | |
23 | classes used by some class are specified during instantiation. | |
24 | ||
25 | Semantics | |
26 | --------- | |
27 | ||
28 | ### Classes | |
29 | ||
30 | A class is a schema describing a set of objects. Instantiating a class | |
31 | produces a new object of that class. When a class is instantiated, all | |
32 | classes that are referenced by the class (dependant classes) must be | |
33 | named (injected) by the instantiating code (the instantiator). For each | |
34 | requested dependant class, any subclass of it may be supplied by the | |
35 | instantiator, further specifying and constraining behaviour (a technique | |
36 | called dependency injection). In this way, classes are inherently | |
37 | parameterized. | |
38 | ||
39 | When a class refers to itself, it is considered a dependant class of | |
40 | itself; it (or a subclass) must be injected by the instantiator. | |
41 | ||
42 | All specified dependant classes must be unique (no dependant class may | |
43 | be specified more than once.) Final classes need not and may not be | |
44 | specified as dependant classes because, being final, there is no | |
45 | subclass that the instantiator could possibly substitute for them. | |
46 | ||
47 | Each class may define zero or more properties and zero or more methods. | |
48 | ||
49 | #### Inheritance | |
50 | ||
51 | Whenever a class is defined in Unlikely source code, a superclass must | |
52 | be named; the class being defined is thus a subclass that inherits from | |
53 | that superclass. Its inheritance consists of the properties and methods | |
54 | defined by the superclass and all of its ancestors (i.e. including all | |
55 | properties and methods that the superclass inherited from its own | |
56 | superclasses) as well as the dependant classes of the superclass. The | |
57 | subclass may not inject dependencies when inheriting from a superclass. | |
58 | Only single inheritance is supported. | |
59 | ||
60 | A subclass may override methods that it inherits from its superclass. It | |
61 | may access the method definition of its direct superclass (or any | |
62 | indirect ancestor class) by naming that superclass explicitly in a | |
63 | continue. | |
64 | ||
65 | A class may be declared as final, in which case it may not be | |
66 | subclassed. In addition, final dependant classes may not be injected. | |
67 | ||
68 | A class may also be declared as saturated, in which case it can be | |
69 | subclassed, but subclasses of it must also be declared as saturated, and | |
70 | they cannot define any new methods. They can only override existing | |
71 | methods. In fact, the root class `Continuation` is declared saturated, | |
72 | so really, all objects have exactly one method, `continue`. | |
73 | ||
74 | If a class defines or inherits any abstract methods, that class must be | |
75 | declared as abstract. Abstract classes cannot be instantiated. Any | |
76 | subclass of an abstract class must define all inherited abstract methods | |
77 | in order to be considered concrete and thus instantiatable. | |
78 | ||
79 | ### Properties | |
80 | ||
81 | Each property has a particular type, which is a class. The values it may | |
82 | take on are objects of that class, or of its subclasses. | |
83 | ||
84 | Each object has its own instances of the properties defined on the | |
85 | class, and each of these properties may take on different values. | |
86 | ||
87 | All state of an object is stored in its properties. Properties are | |
88 | effectively public; they can be modified by code in any method in any | |
89 | class. | |
90 | ||
91 | The root class `Continuation` defines one property, `accumulator`, of | |
92 | type `Passive`, which all classes inherit. | |
93 | ||
94 | Subclasses may not override inherited properties. | |
95 | ||
96 | ### Methods | |
97 | ||
98 | A method is a label on a piece of code inside a class. The methods of a | |
99 | class are shared by all objects of that class. | |
100 | ||
101 | A method may be declared abstract instead of defining code for it. | |
102 | Classes which contain abstract methods must themselves be declared | |
103 | abstract. | |
104 | ||
105 | Only one thing may be done to a method in code, which is to continue it | |
106 | with respect to some object; this is described in the next section. | |
107 | ||
108 | A method may declare zero or more arguments. Each argument has a type, | |
109 | which is a class. When a method is continued, a value of the | |
110 | corresponding type (or some subclass of that type) must be given for | |
111 | each argument. In actuality, the arguments merely name properties of the | |
112 | object; they must already be declared in the class before they are | |
113 | listed in the declaration of the method. Passing values in arguments is | |
114 | just shorthand for assigning these properties before continuing the | |
115 | method. | |
116 | ||
117 | Methods do not have local variables, so for storage must use the | |
118 | properties of the object on which they were continued. | |
119 | ||
120 | The root class `Continuation` defines one abstract method which | |
121 | subclasses must implement, called `continue(Passive accumulator)`. By | |
122 | convention, this method is what other methods continue. The accumulator | |
123 | is passed from continuation to continuation, manipulated as execution | |
124 | proceeds. | |
125 | ||
126 | ### Code | |
127 | ||
128 | The code inside a class labelled by a method consists of a series of | |
129 | assignments followed by a continue. | |
130 | ||
131 | Each assignment consists of an object property on the left-hand side, | |
132 | and an expression on the right-hand side. The the property so named will | |
133 | take on the value resulting from evalulating the given expression. | |
134 | Expressions consist of instantiations of new objects, and references to | |
135 | other object properties. | |
136 | ||
137 | The continue names a method on an object to which control flow will | |
138 | immediately pass. It may also pass values as arguments to that method; | |
139 | however, this is mere shorthand for assigning properties of the object | |
140 | on which the method being continued is defined. See above under | |
141 | "Methods" for more details. | |
142 | ||
143 | ### Passive Data | |
144 | ||
145 | Passive data values, such as integers and strings, are modelled somewhat | |
146 | specially in Unlikely, in order to strike a balance in language design | |
147 | between "too straightforward" and "too convoluted". | |
148 | ||
149 | All passive data values are instances of some subclass of the abstract | |
150 | class `Passive`, which is itself a subclass of `Chain`. When a passive | |
151 | data value is continued, it passes its own value into the accumulator of | |
152 | the "next" continuation. (It is not necessary, however, to continue the | |
153 | passive data value to obtain its value in all cases.) | |
154 | ||
155 | Each immediate subclass of `Passive` gives a data type of values, such | |
156 | as `Integer` or `String`. Each of these type-classes has a countably | |
157 | infinite number of subclasses, one for each possible value of that type. | |
158 | It is these classes that are instantiated to acquire a passive value | |
159 | object. For example, `three = new 3()` instantiates the value 3. When | |
160 | the `continue` method on this object is continued, the value that it | |
161 | represents will be passed down the chain to the continuation assigned to | |
162 | its `next` property. For example, `three.next = new Print()` would cause | |
163 | 3 to be printed when `three` was continued. | |
164 | ||
165 | None of the direct subclasses of `Passive` can be further subclassed. In | |
166 | effect, they are final (despite being abstract!) because all possible | |
167 | subclasses of them already exist. | |
168 | ||
169 | Syntax | |
170 | ------ | |
171 | ||
172 | ### Overview | |
173 | ||
174 | The overall schema of a class definition is: | |
175 | ||
176 | class ClassName(ClassName,ClassName) extends ClassName { | |
177 | ClassName propname; | |
178 | method methodname(ClassName propname, ClassName argname) { | |
179 | propname = new ClassName(ClassName,ClassName); | |
180 | propname.propertyname = argname; | |
181 | goto expr.methodname(expr,expr); | |
182 | } | |
183 | } | |
184 | ||
185 | ### Grammar | |
186 | ||
187 | A somewhat more formal definition of the syntactic structure of Unlikely | |
188 | code is given in the following EBNF-like grammar. | |
189 | ||
190 | ClassBase ::= {ClassDefn}. | |
191 | ClassDefn ::= "class" ClassName<NEW> "(" [ClassName {"," ClassName}] ")" "extends" ClassName | |
192 | ["{" {PropDefn} {MethodDefn} "}"] ["is" ClassMod {"and" ClassMod}]. | |
193 | ClassMod ::= "final" | "saturated" | "abstract". | |
194 | PropDefn ::= ClassName PropName<NEW> ";". | |
195 | MethodDefn ::= "method" MethodName<NEW> "(" [ParamDecl {"," ParamDecl}] ")" | |
196 | ("{" {Assignment} Continue "}" | "is" "abstract"). | |
197 | ParamDecl ::= ClassName PropName. | |
198 | Assignment ::= QualName "=" Expr ";". | |
199 | Continue ::= "goto" PropName "." MethodName "(" [Expr {"," Expr}] ")" ";". | |
200 | Expr ::= ConstrExpr | QualName. | |
201 | ConstrExpr ::= "new" (ClassName | Constant) "(" [ClassName {"," ClassName}] ")". | |
202 | QualName ::= PropName {"." PropName}. | |
203 | ClassName ::= <<sequence of alphabetic characters>> | Constant. | |
204 | Constant ::= <<sequence of decimal digits>> | <<sequence of arbitrary characters between double quotes>>. | |
205 | ||
206 | Note that multiple ClassDefns for a single class may appear; each may | |
207 | partially define the class. In this way a "forward declaration" of a | |
208 | class may occur. This may give its name, superclass, and dependant | |
209 | classes, so these can be consumed by some following class that requires | |
210 | them, before the methods of this class are defined. The dependant | |
211 | classes are cumulative over successive partial definitions; they need | |
212 | not be repeated. However the same superclass must be specified for all | |
213 | partial definitions of a class. | |
214 | ||
215 | Built-in Classes | |
216 | ---------------- | |
217 | ||
218 | - `Continuation` | |
219 | ||
220 | The abstract base class that is the superclass of all Unlikely | |
221 | classes, and which can't be instantiated. Declares the property | |
222 | `Passive accumulator`, and the abstract method | |
223 | `continue(Passive accumulator)`, which all concrete subclasses must | |
224 | implement. Is declared `saturated`, so no subclass may declare or | |
225 | define any additional methods. | |
226 | ||
227 | - `Program` | |
228 | ||
229 | An abstract continuation that provides the guarantee that it can | |
230 | be started (that is, that its `continue` method can be initially | |
231 | continued) from the operating system. It can be reasonably | |
232 | expected that the `accumulator` will be assigned a value | |
233 | provided by the user, perhaps via a command-line argument. | |
234 | ||
235 | - `Stop` | |
236 | ||
237 | A concrete continuation which exits to the operating system | |
238 | when continued. The accumulator is used as the exit code, | |
239 | assuming the operating system supports this. | |
240 | ||
241 | - `Chain` | |
242 | ||
243 | An abstract continuation which defines the property | |
244 | `Continuation next`. When a subclass of `Chain` is | |
245 | continued, it will generally continue the continuation | |
246 | assigned to its `next` property after doing something. | |
247 | ||
248 | - `Passive` | |
249 | ||
250 | The abstract final base class representing passive data | |
251 | values. Discards whatever accumulator was passed to it, | |
252 | and passes its own value into the accumulator when it | |
253 | continues the continuation assigned to its `next` | |
254 | property. | |
255 | ||
256 | - `Boolean` | |
257 | ||
258 | The abstract final base class representing boolean | |
259 | values. | |
260 | ||
261 | - `True`, `False` | |
262 | ||
263 | Final classes representing particular boolean | |
264 | values. | |
265 | ||
266 | - `Integer` | |
267 | ||
268 | The abstract final base class representing integer | |
269 | values. | |
270 | ||
271 | - `0`, `1`, `2`... | |
272 | ||
273 | Final classes representing particular integer | |
274 | values. Note that only positive constants are | |
275 | available; negative values must be computed by | |
276 | subtracting from 0. | |
277 | ||
278 | - `String` | |
279 | ||
280 | The abstract final class representing string values. | |
281 | ||
282 | - `""`, `"a"`, `"b"`, `"aa"`... | |
283 | ||
284 | Final classes representing particular string | |
285 | values. | |
286 | ||
287 | - `BinaryOperation` | |
288 | ||
289 | A continuation which posesses a property `value` and | |
290 | which yields a value when continued by applying some | |
291 | operation to `value` (treated as LHS) and the | |
292 | accumulator (treated as RHS.) | |
293 | ||
294 | - `Add` | |
295 | ||
296 | A concrete binary operation which adds `value` to | |
297 | the accumulator and passes the result when | |
298 | continuing `next`. | |
299 | ||
300 | - `Subtract` | |
301 | ||
302 | A concrete binary operation which subtracts the | |
303 | accumulator from `value` and passes the result when | |
304 | continuing `next`. | |
305 | ||
306 | - `Multiply` | |
307 | ||
308 | A concrete binary operation which multiplies `value` | |
309 | by the accumulator and passes the result when | |
310 | continuing `next`. | |
311 | ||
312 | - `Divide` | |
313 | ||
314 | A concrete binary operation which divides `value` by | |
315 | the accumulator and passes the result when | |
316 | continuing `next`. | |
317 | ||
318 | - `Condition` | |
319 | ||
320 | A BinaryOperation which yields a boolean value. | |
321 | ||
322 | - `Equal` | |
323 | ||
324 | A concrete binary operation which compares | |
325 | `value` to the accumulator and passes True when | |
326 | continuing `next` only if they are equal. | |
327 | ||
328 | - `GreaterThan` | |
329 | ||
330 | A concrete binary operation which compares | |
331 | `value` to the accumulator and passes True when | |
332 | continuing `next` only if `value` is greater. | |
333 | ||
334 | - `Print` | |
335 | ||
336 | A concrete continuation which displays the value of the | |
337 | accumulator to the user before continuing its `next` | |
338 | property. | |
339 | ||
340 | - `Input` | |
341 | ||
342 | A concrete continuation which obtains a value from the | |
343 | user, and passes this value in the accumulator when | |
344 | continuing its `next` property. | |
345 | ||
346 | - `Branch` | |
347 | ||
348 | An abstract continuation which continues one of the | |
349 | continuations assigned to its properties, based on some | |
350 | other information. Defines the `Chain` property `else` | |
351 | which represents the basic alternate continuation that | |
352 | can be continued instead of `next`. | |
353 | ||
354 | - `If` | |
355 | ||
356 | A continuation which continues one of two | |
357 | continuations assigned to its properties, `next` and | |
358 | `else`, based on whether the accumulator is an | |
359 | instance of `True` or `False`. | |
360 | ||
361 | - `Switch` | |
362 | ||
363 | An abstract continuation whose behaviour differs on | |
364 | subsequent times it is continued. This is | |
365 | implemented by means of a `state` property which | |
366 | changes values each time the switch is continued; | |
367 | depending on the value of `state`, different things | |
368 | happen. | |
369 | ||
370 | - `Loop` | |
371 | ||
372 | An abstract continuation which is intended to | |
373 | implement a loop. However, this is not | |
374 | automatic; some continuation chain leading from | |
375 | this continuation *must* lead back to this | |
376 | continuation in order for actual repetition to | |
377 | take place. | |
378 | ||
379 | - `WhileLoop` | |
380 | ||
381 | A concrete `Loop` which, on odd visits | |
382 | continues its `test` property. It is assumed | |
383 | that some continuation down that chain | |
384 | continues this `WhileLoop` object with a | |
385 | boolean value in the accumulator. On these | |
386 | even visits, it will behave like an `If`: | |
387 | when the boolean is a `True`, `next` is | |
388 | continued, otherwise `else`. | |
389 | ||
390 | - `ForLoop` | |
391 | ||
392 | A concrete `Loop` which defines `value`, | |
393 | `delta`, and `finish` properties, all | |
394 | `Integer`s. On each visit, it checks if | |
395 | `value` equals `finish`; if not, it adds | |
396 | `delta` to `value` and continues `next`. But | |
397 | if so, it simply continues `else`. | |
398 | ||
399 | Implementations | |
400 | --------------- | |
401 | ||
402 | There is not currently a reference implementation of Unlikely. Any | |
403 | contradictions and/or grey areas in this document will therefore have | |
404 | nowhere to turn to to be cleared up. | |
405 | ||
406 | There is, however, a partial and non-normative implementation of | |
407 | Unlikely, a static analyzer written in Python called Coldwater. It | |
408 | parses Unlikely programs and identifies many type errors and other | |
409 | statically-detectable invalid programs. It is meant to give some body to | |
410 | the ideas present in Unlikely, without actually going so far as | |
411 | implementing it in full. | |
412 | ||
413 | The construction of Coldwater helped clear up some of the fuzzier | |
414 | corners of the language. However, there are probably several areas that | |
415 | remain fuzzy and render the language unsuitable for anything but the | |
416 | most trivial programming. Extending Coldwater to be a full-fledged | |
417 | Unlikely interpreter will probably help define the language. However | |
418 | that project has been deferred as future work, and any clarifications | |
419 | that come from it will be incorporated only in a future language | |
420 | version. | |
421 | ||
422 | Discussion | |
423 | ---------- | |
424 | ||
425 | This section contains some random thoughts and reflections on the | |
426 | Unlikely programming language. | |
427 | ||
428 | There is a rough analogy between Unlikely's requisite dependency | |
429 | injection class parameters, and value parameters in languages without | |
430 | global variables. There is no implicit referent when you say `Foo`; | |
431 | `Foo` must name some parameter that has been passed into scope. | |
432 | ||
433 | Because all the parts of the language are modelled as objects, the | |
434 | language's execution model has some resemblance to an object-oriented | |
435 | AST interpreter for itself. Except, of course, these objects are | |
436 | continuations, so it is not like a recursive, depth-first walk of the | |
437 | AST; it is much closer to the technique of threading the AST and | |
438 | following that thread. | |
439 | ||
440 | At one point I included the constraint that the set of dependant classes | |
441 | specified by a class must be mutually disjoint; that is, no dependant | |
442 | class in the set could be a subclass of some other dependant class in | |
443 | the set. I am not entirely sure why I introduced that constraint, since | |
444 | it could well be valuable to refine two classes by injection even when | |
445 | one of those classes is a subclass of the other. I took it out. | |
446 | ||
447 | Because properties cannot be redefined in subclasses, and because | |
448 | parameters to methods are just syntactic sugar for properties, methods | |
449 | cannot be overloaded. In particular `continue` must always work on a | |
450 | `Passive` parameter, although of course it is mere convention that the | |
451 | method works on the `accumulator` property anyway. | |
452 | ||
453 | Unlikely is Turing-complete. (I will assert this because it seems | |
454 | reasonable to me, but I do not have a proof, so I may be wrong.) The | |
455 | Unlikely language is not minimal. In particular, the `Loop` class and | |
456 | its subclasses `WhileLoop` and `ForLoop` could be removed, and the | |
457 | resulting language would still be Turing-complete. In fact, `WhileLoop` | |
458 | and `ForLoop` could probably be implemented in Unlikely and provided in | |
459 | an extension class library. | |
460 | ||
461 | The idea to conflate contintuations and objects was inspired by the | |
462 | data-structure representation of continuations in Chapter 7 of | |
463 | [Essentials of Programming Languages, 2nd | |
464 | ed.](http://www.cs.indiana.edu/eopl/), which embodies a tiny measure of | |
465 | inheritance. The idea that language constructs have commensurate | |
466 | inheritance relationships (`WhileLoop` and `ForLoop` are subclasses of | |
467 | `Loop`, etc.) was borrowed from Steve Yegge's article [Scheming is | |
468 | Believing](http://steve.yegge.googlepages.com/scheming-is-believing). | |
469 | The idea that all programs are subclasses of `Program`, which dovetails | |
470 | so nicely with that, was borrowed from Brian Connors' "Sulawesi" | |
471 | language design. The idea that every concrete value has its own class, | |
472 | leading to abstract final classes with countably infinite subclasses, | |
473 | was pure desperation. | |
474 | ||
475 | For the purpose of defining computable functions, the Unlikely-Calculus | |
476 | could be considered as a variant of Unlikely without `Print` or `Input` | |
477 | classes. The `Stop` class would be there redefined to yield the value | |
478 | passed to the accumulator as the result of evaluation, rather than as an | |
479 | operating system exit code. | |
480 | ||
481 | It's a safe bet that there is at least one person out there who will be | |
482 | disappointed that this language is named Unlikely yet contains no | |
483 | probabilistic design features. | |
484 | ||
485 | Happy object-method-continuing! | |
486 | Chris Pressey | |
487 | March 15, 2009 | |
488 | Bellevue, WA |
19 | 19 | exit 1 |
20 | 20 | fi |
21 | 21 | |
22 | falderal $APPLIANCES tests/Unlikely.markdown || exit 1 | |
22 | falderal $APPLIANCES tests/Unlikely.md || exit 1 |
0 | Tests for Unlikely | |
1 | ================== | |
2 | ||
3 | -> Tests for functionality "Parse Unlikely Program" | |
4 | ||
5 | Here is a syntactically correct program. | |
6 | ||
7 | | class Count(Count,Chain,Print,Add) extends Continuation | |
8 | | | |
9 | | class CountForever(Count,Chain,Print,Add) extends Program { | |
10 | | Count c; | |
11 | | method continue(Passive accumulator) { | |
12 | | c = new Count(Passive,Count,Chain,Print,Add); | |
13 | | goto c.continue(new 1(Passive)); | |
14 | | } | |
15 | | } | |
16 | | | |
17 | | class Count() extends Continuation { | |
18 | | Count c; | |
19 | | Print p; | |
20 | | Add a; | |
21 | | method continue(Passive accumulator) { | |
22 | | c = new Count(Passive,Count,Chain,Print,Add); | |
23 | | a = new Add(Passive,Chain); | |
24 | | a.value = new 1(Passive); | |
25 | | a.next = c; | |
26 | | p = new Print(Passive,Chain); | |
27 | | p.next = a; | |
28 | | goto p.continue(accumulator); | |
29 | | } | |
30 | | } | |
31 | | | |
32 | = | |
33 | ||
34 | And here is one which is not syntactically correct. | |
35 | ||
36 | | class Hello(Print,Chain,Stop) extends Program { | |
37 | | Print p; | |
38 | | method continue(accumulator) { | |
39 | | p = new Print(Passive,Chain); | |
40 | | p.next = new Stop(Passive); | |
41 | | goto p.continue(new "Hello, world!"(Passive)); | |
42 | | } | |
43 | | } | |
44 | ? ArtefactNotFoundError |
0 | Tests for Unlikely | |
1 | ================== | |
2 | ||
3 | -> Tests for functionality "Parse Unlikely Program" | |
4 | ||
5 | Here is a syntactically correct program. | |
6 | ||
7 | | class Count(Count,Chain,Print,Add) extends Continuation | |
8 | | | |
9 | | class CountForever(Count,Chain,Print,Add) extends Program { | |
10 | | Count c; | |
11 | | method continue(Passive accumulator) { | |
12 | | c = new Count(Passive,Count,Chain,Print,Add); | |
13 | | goto c.continue(new 1(Passive)); | |
14 | | } | |
15 | | } | |
16 | | | |
17 | | class Count() extends Continuation { | |
18 | | Count c; | |
19 | | Print p; | |
20 | | Add a; | |
21 | | method continue(Passive accumulator) { | |
22 | | c = new Count(Passive,Count,Chain,Print,Add); | |
23 | | a = new Add(Passive,Chain); | |
24 | | a.value = new 1(Passive); | |
25 | | a.next = c; | |
26 | | p = new Print(Passive,Chain); | |
27 | | p.next = a; | |
28 | | goto p.continue(accumulator); | |
29 | | } | |
30 | | } | |
31 | | | |
32 | = | |
33 | ||
34 | And here is one which is not syntactically correct. | |
35 | ||
36 | | class Hello(Print,Chain,Stop) extends Program { | |
37 | | Print p; | |
38 | | method continue(accumulator) { | |
39 | | p = new Print(Passive,Chain); | |
40 | | p.next = new Stop(Passive); | |
41 | | goto p.continue(new "Hello, world!"(Passive)); | |
42 | | } | |
43 | | } | |
44 | ? ArtefactNotFoundError |