atomo programming language (fork of alex's atomo) — http://atomo-lang.org/
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 | #title:{Class Objects}tag:"class-objects"
The class-objects system is a trivial implementation of modules, mixins, and classes, included in the prelude.
Classes defined with this system are essentially alternative syntax for creating objects and their methods. Thus, you're always just dealing with objects, and to the outside world there is no real distinction between those defined with this system and those not.
Defining a module (see #hl:{module:}) registers its body associated with its name into the #reference:{Macro Environment}. An #hl:{include:} dispatch expands that module's body "onto" its target.
Class object and module bodies used with these macros are very similar - indeed, they are evaluated with the exact same translations, just in different contexts. A class definition's body is evaluated upon class creation, to set up the meta-object. A module's body is evaluated wherever it is #hl:{include:}d.
Inside of these definition bodies, #hl:{:=} has a certain context implied for the right hand side. For class objects, it's the object receiving the message, and for modules it's the target being included onto. This context can be referred to explicitly as #hl:{me}, which is similar to #code:{self} or #code:{this} in other languages (but it's not a keyword in Atomo).
In addition to this rule, methods named #code:{new} or beginning with #code:{new.} are assumed to be initializers. Their right-hand side should be a block containing expressions to evaluate on the new instance.
#section:{Defining Classes}
#define:{
class: body &extends: Object
| body is-a?: Block
> Object
}body:{
Creates an anonymous class object with a given body, cloning a specified parent, or #hl:{Object} by default.
#example:{
X = class: { new := { foo = 1 } }
X new foo
Y = class: { new := { foo = 1 } } &extends: 1
Y is-a?: 1
Y new + 2
}
}
#define:{
class: x
| x is-a?: Dispatch
> Object
}body:{
Creates a class object, specifying a name along with the body. The full form is #hl:{class: A: { ... }}; hence #hl:{x} is a keyword dispatch, with the message name being the name of the class.
If the class does not exist, it is defined as the given name. If it does exist, the classes body is evaluated on it, behaving very similarly to the "reopening" semantics found in Ruby.
Class objects defined with this method get a #hl:{pretty} definiton for free, which pretty-prints as the classes name. This can be overridden in the class body.
#example:{
class: X: { new := { foo = 1 } }
X new
X new foo
}
}
#define:{
me
> any
}body:{
When running a method, this is bound to the to the current object instance or. In the class body, this refers to the meta-object.
}
#section:{Modules & Mixins}
#define:{
module: x
| x is-a?: Dispatch
> @ok
}body:{
Registers a module, specifying the name along with the body. The full form is #hl:{module: A: { ... }}; hence #hl:{x} is a keyword dispatch, with the message name being the name of the module.
This macro just expands to #hl:{@ok}; its sole use is for its side-effects.
#example:{
module: A: { foo = 1 }
}
See #hl:{include:} for where things get interesting.
}
#define:{
target include: name
| name is-a?: Dispatch
> any
}body:{
Expand a module's body onto #hl:{target}, finding the module via #hl:{name}.
If the module cannot be found, #hl:{@unknown-module:} is signaled.
#example:{
module: A: { foo = 1 }
include: A
foo
include: B
}
When used inside of a module or class object body, it specifically targets the meta-object or module target, respectively. That is, a module can be included into another module or into a class.
#example:{
module: Subtractable: { sub: x := add: (-1 * x) }
class: Adder: { include: Subtractable; add: n := @(adding: n) }
Adder add: 10
Adder sub: 10
}
This mixing-in can also be done on an arbitrary target.
#example:{
module: Subtractable: { sub: x := add: (-1 * x) }
class: Adder: { add: n := @(adding: n) }
Adder include: Subtractable
Adder add: 10
Adder sub: 10
}
} |