Misty is an object-oriented language but it is not a classical language: Objects are not rigidly defined by classes. Instead, Misty's objects are soft and malleable. Misty objects can have members added or removed at any time unless the object is fixed.
Misty unifies objects and associative data structures. Objects are unordered containers of name/value pairs. Objects are namespaces.
Objects are mutable, but can be made immutable by the fix prefix
operator. Objects are passed by reference.
Members can be accessed using either a dot notation or a bracket notation.
There are two ways to make a new object: object literals or the object
operator.
A new object can be made with an object literal.
object_literal => '{' pair,
'}'
pair
=> name [':' expression |
parameter_list function_body]
=> text [':' expression]
In the object literal notation, the specification of an object begins
with { (left brace) and ends with } (right
brace). Between them are zero or more name/value pairs, separated
by , (comma). A name/value pair is an
identifier or text, followed by : (colon)
followed by an expression. (See JSON.) Each pair
contributes a member to the object.
If the value is a function, then the abbreviated method form can be used. See Functions.
If no value is supplied, then the value of the member is assumed to
be true. This is useful when using objects as sets.
An empty object can be made by {} (braces).
empty_object := {} # empty_object is {}
color := { # color is {
aliceblue, # "aliceblue" : true,
antiquewhite, # "antiquewhite" : true,
aqua # "aqua" : true
} # }
The statement:
stooge := {first : 'Curly', last : 'Howard'}
has the same result as
stooge := {}
stooge.first := 'Curly'
stooge.last := 'Howard'
object OperatorThe object prefix operator makes a new mutable object that
inherits from an old object through a delegation linkage. The new object
initially contains no members. Any attempts to access members that it
does not have will be satisfied by the old object. Members that are not
inherited are called own members.
object null produces a new, empty mutable object.
Is is the same as {}.
The members of an object can be accessed with either the dot notation or the subscript notation.
The dot notation is usually the most convenient notation for accessing the members of an object. It takes an object and an identifier.
Example:
stooge := {first : 'Curly', last : 'Howard'}
first_name := stooge.first # first_name is 'Curly'
first_name := null.first # first_name is null
new_stooge := object stooge
new_stooge.first := 'Shemp' # new_stooge is {first : 'Shemp'}
last_name := new_stooge.last # last_name is 'Howard'
The subscript notation is similar to the dot notation, but instead of
taking an identifier (which is used as a text) it can take an expression
of any type except null. It can be used for dynamically making
member names, or for creating keys which are not texts. Type is significant
in keys. The key 0 (a number) is distinct from the key '0'
(a text). A key 'a' (lower case) is distinct from
a key 'A' (upper case). The subscript expression is wrapped
in [ (left bracket) and ] (right
bracket).
The subscript notation is used with objects, arrays, and texts. Using
it with any other type raises a 'type' exception.
Example:
stooge := {first : 'Curly', last : 'Howard'}
first_name := stooge['first'] # first_name is 'Curly'
new_stooge := object stooge
new_stooge['first'] := 'Shemp'
# new_stooge is {first : 'Shemp'}
new_stooge['aka'] := 'Samuel Horwitz'
# new_stooge is {aka : 'Samuel Horwitz', first : 'Shemp'}
See Operators to see more about these operators.
fixThe fix prefix operator returns an immutable reference.
It is not possible to directly modify an object with a fixed reference,
or to extract its methods or functions. A fixed reference allows for read
only access to the data members and invocation of its methods and functions.
hasThe has operator returns true if its first
operand is an object and if its first operand has a member (possibly inherited)
that is named by the second operand.
is dataThe is data operator returns true
for objects.
is fixThe is fix operator returns true
if its operand is immutable.
is objectThe is object operator returns true
for objects.
objectMake a new object that inherits from the operand.
ownsThe owns operator returns true if its first
operand is an object and if its first operand has a member (not inherited)
that is named by the second operand.
Objects can be used as sets. A set is an unordered collection
of names. If we ignore the values in an object, the keys work just like
a set. By convention, we use true as the value of members
of a set but you can use any values you like except null.
An array can be converted into a set object with array.object(true).
The object.keys method can be used to turn a set
object into an array.
The methods union, intersection, and difference
create a new set from two existing sets. One of the sets is always an
object. The other set can be an object, or an array that will be converted
into a set object for you, or null which will be interpreted
as the empty set. The delegation linkage is not used by the set methods.
A Misty system may be working in an environment that provides objects
that work very differently than Misty's objects. Such foreign objects
can be wrapped to act as Misty objects. Such objects will have three methods:
get, set, and call.
The method invocation pattern is
object
.method_name(parameter_list)
The object is searched for a member matching the method_name.
The search skips values which are not functions. If the result of that
search is not a function, then the list of built-in function methods will
be searched. This avoids conflicts in the use of data members that happen
to have the same names as the methods. It is possible, for example, to
use text as both a data member and a method.
define my_object := {text : 'Carl Hollywood'}
result := my_object.text()
# result is '{text: "Carl Hollywood"}'
These are the built-in methods. They are available to all objects except foreign objects. All of these methods may be overridden.
.array(dimension, init)Numbers can be used as keys, so objects can be used as sparse arrays.
The array method can turn a sparse object into a normal array.
It does this by first creating an array of a given size, with all of the
elements initialized to the specified init value. Then every
member in the object with an integer key that fits within the
dimension is copied into the array.
It returns a new array.
.call(method,
arguments)The call method is an alternate way of invocating a method.
It makes it possible to call methods on an object that the
object does not have. The method parameter can be
a method value or a text containing the name of one of the object's
methods. Otherwise a 'type' exception will be raised. If
the object is fixed, then a 'fix' exception will
be raised. If the method parameter is a text, and if the object does
not have a function value associated with that name, then a 'function'
exception will be raised.
If arguments is null, then the method will be
invoked with no arguments. If arguments is an array, then its
values will be passed to the method as arguments. Otherwise, the method
will be called with arguments as the single argument.
It returns the result of calling the method on the object.
Also see the apply operator and the array.call
method.
The call method provides two advantages over ordinary method
invocation. First, it allows for passing the arguments as an array. Secondly,
and more importantly, the call method also works on arrays.
This is useful when a program could be working on either a single object
or an array of objects.
.combine(second)The combine method takes all of the members of a second
object and copies them in this object. If the second
object contain members sharing the same keys, the new values replace the
old ones in this object. If the first object is
fixed, then a 'fix' exception will be raised. Also see the
{} augmentation suffix operator and the object.union
method.
It returns the expanded, modified object.
.difference(second)The difference method treats the object as a set, producing
the difference of the object and a second object
or array.
It returns a new object containing all of the members of the object that do not have the same keys as members of second.
.equal(second)The equal method returns true if the object
and a second object are equal. This is a shallow test of equality:
the objects must both contain
the same key/value pairs. The delegation linkages are not considered in
the comparison.
Examples:
a := {first: 'Carl', last: 'Hollywood'}
b := {first: 'Carl', last: 'Hollywood'}
result := a = b # result is false
result := a.equal(b) # result is true
a.next := a
b.next := a
result := a.equal(b) # result is true
a.next := b
result := a.equal(b) # result is false
.finalize(method)The finalize method registers a finalization method
with the object. Multiple finalization methods can be added
to any object.
When the object is ready to be garbage collected, the finalization methods will be invoked. If a finalization method causes the program to become aware of the object, then the object will not be garbage collected, but all of its finalization methods will be removed.
It returns the object.
.get(name)The get method returns a value from an object. It can be
implemented as
method get(name) {
return $[name]
}
It returns a value.
.intersection(second)The intersection method treats this object as
a set, producing the intersection of this object and a second
object or array.
It returns a new object containing the members of this object that have the same keys as members of the second.
.keys()The keys method makes an array containing all of the own
keys in the object that are texts. The keys are not guaranteed
to be in any particular order. If the object is fixed then
it raises a 'fix' exception.
It returns an array of keys.
.pretty()The pretty method makes a text from the object, adding line
breaks and indentations to make it more readable.
It returns a text.
.remove(key)The remove method removes the own member associated with
the key from the object. If there is no member with
a matching key, then nothing is removed. If key
is an array, then it is treated as an array of keys that will all be removed
from the object.
Changing a member's value to null will also remove it. Also
see the object.difference method.
It returns the modified object.
.set(name, value)The set method stores a value in an object. It could be
implemented as
method set(name, value) {
if $ is fix then
raise 'fix'
fi
$[name] := value
return $
}
It returns the object. It raises a 'fix' exception
if the object is fixed.
.text()The text method makes an Extended JSON text from the object.
It returns a text.
.union(second)The union method treats the object as a set, producing the
union of this object and a second object or array.
It returns a new object that contains all of the members of the object and the second object. Where the objects' keys intersect, this object's members will be used.
.values(keys)The values method returns an array containing the values
of all of the members of the object. If the object is fixed then it raises
a 'fix' exception. If keys is null,
then only own values having keys that are texts are collected. The values
are not guaranteed to be in any particular order; it might not be the
same order as obtained from the object.keys()
method. If keys is an array of keys (usually obtained from
the object.keys method), then the keys
are used to determine the order of the values.
It returns an array of values.
Example:
o := {first: 'Carl', last: 'Hollywood'}
result := o.values() # result is ['Hollywood', 'Carl']
result := o.values(['first', 'last']) # result is ['Carl', 'Hollywood']
| Binary operator | Method |
|---|---|
+ |
add |
- |
subtract |
* |
multiply |
/ |
divide |
÷ |
idivide |
** |
raise |
~ |
concat |
~~~ |
concats |
== |
equal |
< |
lt |
> |
gt |
<= |
le |
>= |
ge |
apply |
apply |
mod |
mod |
min |
min |
max |
max |
| Unary operator | Method |
|---|---|
+ |
number |
- |
negate |
~ |
text |
abs |
abs |
arity |
arity |
int |
int |
name |
name |
not |
not |
parameters |
parameters |
Many of the unary and binary operators can be used on objects that contain
suitable methods. For example, if myobject contains an add
method, then
myobject + value
has the same result as
myobject.add(value)
Similarly,
-myobject
has the same result as
myobject.negate()
This makes it possible to create specialized number types which use objects
to implement special characteristics (such as bignum or complex)
and to use those objects with the normal operators. Note that only two
of these methods (text and equal) are implemented
by default for objects.
Note that = and <> are always concerned
with exactly the same object. Neither operator can be overridden.
The == operator used on an object will call the equal
method, which can be overridden.
$ will be bound to the first operand. Unary methods must
have an arity of 0. Binary methods must have an arity of 1.
The Misty machine uses a get process to retrieve values from an
object. If the object is fixed, then the value obtained will
also be fixed. Also, if the object is fixed, function values will be treated
as null values.
If there is no key in the object that exactly matches the
key specified in the dot notation or subscript notation (as determined
by the = (equal sign) operator), then
the result is null, not an error or exception. This makes
it easy to query an object.
If the object was created by the object operator,
then it may be linked to a delegate object. If the search fails, then
the delegate object will be searched. If the linked object is itself linked,
then the search can continue on. If the delegate chain is exhausted, then
the result is null.
The Misty machine uses a call process to retrieve function values from an object. The call process is similar to the get process with a couple of differences.
The call process is only interested in function values. If it finds a value that is not invocable, it ignores it and continues looking at the delegate chain.
If the delegate chain is exhausted, it will look to the built-in object methods.
The is function operator uses the call
process rather than than the get process.
The Misty machine uses a put process to store values into an object.
If the object is fixed, then a 'fix' exception
is raised unless the object being modified is $ and if the
current method was called with the method invocation pattern and not by
the call or apply methods. If the key text
already exists in the object, then the value is replaced. If
the key text does not already exist in the object, the new
key and value are added. If the value being stored is null,
then the member is deleted. The delegate object plays no role in the put
process.