Features of Cecil

Cecil has multimethods, which generalize singly dispatched receiver-oriented languages to allow dynamic dispatching on any subset of a method's arguments. Dispatched arguments are treated symmetrically, unlike Common Lisp where earlier arguments are more important than later arguments.

Cecil has a classless object model. Objects have methods attached directly to them, without needing a separate class to hold their format & behavior. One-of-a-kind objects with their own behavior (e.g. true, false, nil, and other enumerated constants) are easy to define.

Cecil has first-class lexically-nested anonymous functions, a la Smalltalk blocks, Scheme lambdas, and ML functions. Function values are heavily used in Cecil code for user-defined control structures, iterators, exception handlers, typecase-like constructs, and more.

Cecil has both immutable (the default) and mutable versions of local, global, and instance variables (called fields in Cecil). Immutable variables support clearer, more understandable code. Initial values of variables are specified at the point of declaration, except that for fields initial values can be specified when the containing object is created.

Cecil accesses both fields (instance variables) and methods via dynamically dispatched messages. Each field declaration implicitly generates a get and (if mutable) a set accessor method providing the sole means for reading and writing the field's contents. By accessing fields and methods uniformly, implementors can easily change between storing and computing some value w/o modifying clients, and methods can be overridden with fields and vice-versa.

Cecil has predicate objects, which allow virtual "subclasses" to be defined carrying specialized methods that override their parent's methods whenever some boolean predicate over the object is true. In this way, inheritance can be used to model time-varying and/or state-dependent properties of objects just like permanent properties of objects.

Cecil supports multiple inheritance. The inheritance graph is treated as a partial order, inducing a corresponding partial order on methods attached to those objects. This design results in the simple rule that a method attached to a child overrides any method attached to an ancestor.

Cecil's object declarations do not "contain" their method, field, or even parent declarations. Instead, all these attributes of objects are declared externally, allowing clients to add methods, fields, and even parents of existing objects separately from their original definition.

Cecil supports a polymorphic static type system. Types and subtyping is semantically distinct and independent from from objects and inheritance, although syntactic sugar makes declaration of parallel subtyping and inheritance easy. Any declaration can be polymorphic over some set of type parameters, and constraints can be placed on these type parameters to limit the set of legal instantiating types to those that satisfy needed properties; Cecil's constraint-based bounds subsume (F-)bounded polymorphism and where clauses.

Cecil's type declarations are optional. Where type declarations are present, static type checking ensures that the type declarations are internally consistent. Where omitted, dynamic type checking guarantees run-time type safety.

History of Cecil

The initial Cecil design effort was started in early 1991. The first Cecil interpreter and typechecker implementation, written in Self, was done over 1992 and 1993. In 1993, a translator from Cecil to C was added to the interpreter, making the first Cecil compiler. In 1994, a larger Cecil optimizing compiler, this time written in Cecil, was begun; this compiler eventually evolved into the Vortex optimizing compiler.

Some resources for Cecil