Warning: this is superseded! Please visit the Ix site instead.
The information in this and future posts is likely to change many times as the Ix design matures. The purpose of these posts, apart from documenting my ideas, is to solicit comments, which hopefully will lead to improvements.
Significant changes in design will merit new posts; otherwise, old posts will be updated.
This post deals with the Ix memory model and the nature of objects, functions, and tasks. It introduces a lot of information rather quickly, as a preparation for future posts.
This post doesn't discuss syntax yet, just semantics.
Objects and functions
Everything in Ix is both an object and a function.
All objects support these operations, and no others:
- listattr - list attributes (names + metadata).
- getattr, setattr - get, set attribute value by name.
- call - call the object, as a function.
An object is, in theory, opaque; it may implement these operations in any way it chooses. In practice, most objects will be defined in the ordinary way - with a statically known, immutable list of attributes, enabling code completion, type inference, and efficient implementation.
A function is an object that implements the call() method and may have a few attributes provided by the runtime, such as a name. Objects not intended to be used as functions will not support the call() operation, but an object with custom attributes that also implements call() would be indistinguishable from a function some attributes added.
Tasks
A running Ix program consists of one or more tasks, in the .Net meaning of the word: bits of code that can be run on native threads. Tasks are extremely lightweight in terms of the memory and time used to create a task. They are at least reasonablly lightweight in terms of switching (scheduling).
A task holds references to one or more pipes. Pipes are used to send and receive objects between tasks. A task can have pipes connecting it with any child tasks it creates and with its parent task. References to other pipes can also be passed through pipes.
A task cannot influence the outside world or be observed by it except through pipes (but see below for IO, which needs special handling).
Mutability
The runtime maintains a mutability flag for each object. An immutable object can only refer to other immutable objects, and cannot be changed in any way. A type (of a name) can also be mutable or immutable: this simply means it does or does not include method calls that mutate the object (e.g., setters), similar to const variable types in C++.
Immutable objects can be shared between tasks freely (by passing them through pipes, or at task creation). Mutable objects must always be owned by exactly one task.
Changing mutability
A mutable object can be 'frozen' to become immutable. The owning task should prove to the compiler/runtime that no references to the object remain assigned to variables whose type is mutable. If the operation is forced without proving this, then future attempts to mutate the object will fail.
This is not the same as voluntarily restricting the type of a reference (name) from mutable to immutable - that would be a simple upcast. Mutability is a property of the object separate from the mutability of the type of a reference to it.
An immutable object can be 'unfrozen' to become mutable. The task asking to unfreeze it should prove to the compiler/runtime that no other task has a reference to the object. Otherwise, a runtime check must be performed for such references before making the object mutable and owned by the requesting task. (If no implementation for proving such things is implemented, then the runtime check will always be performed.)
Sending objects through pipes
An immutable object can be sent through a pipe by reference; this is very cheap.
A mutable object can be sent through a pipe by reference by transferring ownership of the object to the receiving task. The sending task should prove to the compiler/runtime that it is not keeping a reference to the object being sent. If it does not prove this and forces the operation, any future access to this object by the sending task will fail.
Cloning
Any object can be cloned (except for IO objects, see below). The language is aware of an object's structure and can always clone it directly; unlike in other languages, objects are clonable by default. An object may provide a method to modify the newly created clone (useful if an object e.g. manages unique IDs), but if at all possible, this method should not make the operation fail.
(I have considered outright forbidding the method from failing, but this seems on balance counterproductive.)
Ix supports shallow, deep, and in-between (user-visitor-controlled) cloning.
When creating a deep clone, it can be immediately made mutable or immutable as required, no matter what the mutability state of the original object.
Ix supports COW (copy on write) and can use it for cloning, among other uses.
IO objects and tasks
The fundamental reason IO tasks must be tracked and managed is that we cannot generally implement transaction rollback for external IO, and transactions are a core feature of Ix.
Any task that communicates with things outside the process must be known to the runtime as an IO task.
Certain objects (probably only special ones provided by the runtime) which initiate IO - such as objects for opening files or sockets - are marked as IO objects. IO objects are always mutable, and any task that owns one is an IO task.
In well-designed Ix programs, IO should always be separated into its own tasks, which should be kept as small as possible. Minimizing 'IO contamination' will ensure that the maximum amount of code will still receive the benefits of transactionality.
Transactions
Ix uses COW (copy on write) extensively to provide transactions. This functionality may or may not become an STM (software transactional memory) mechanism; I need to learn more about STM implementations to grok fully how other people do this.
This part still needs to be fleshed out. To fully describe transactional behavior, I also need to finish writing the spec for method visibility, which in turn will require the spec for the typing system, which might turn out to be rather long. So this is a convenient point to stop for tonight :-)
More tomorrow!
0 comments:
Post a Comment