This project has moved. For the latest updates, please go here.

Essence# Class Libraries

A class library is a set of namespaces, classes, traits, global constants and global variables that reside in a persistent store. Currently, a class library only exists as source code, but it should be possible (eventually) to also store class libraries in compiled form in .Net .DLL files. That's one work item for which we would be happy to accept the work of contributors having the requisite motivation and skill set.

Any number of different class libraries may be loaded into the same execution context.

Different class libraries may contain "the same" namespaces, "the same" global variables, "the same" traits and "the same" classes, and the traits and/or classes may contain the same methods. In fact, two different class libraries might contain exactly the same code. Or they might be completely different. More typically, they would mostly contain different namespaces, global variables, traits, classes and methods, with only a few selected items being jointly defined.

The root namespace is always a default, virtual member of every class library. It does not need to be declared, and in fact cannot be.

If two class libraries that are loaded into the same execution context declare "the same" namespace, "the same" global variable, "the same" trait or "the same" class, then that namespace, global variable, trait or class will only be created once. Any modifications specified to "the same" namespace, global variable, trait or class by one class library will affect that global variable, trait, class or namespace globally within the execution context of the running program. A namespace, global variable, trait or class declared in one class library is "the same" as one declared in another if their fully-qualified names are identical.

In other words, a class library defines a unit of code that can be loaded atomically into an execution context. When using the ES tool for running Essence# scripts from the Windows Command Prompt, or when using the DLR's hosting protocol, the Standard Library is always loaded into the execution context of an Essence# program--but that's not an architectural requirement of the runtime system. Loading any additional libraries is optional. See the section on running a script for more details.

It's possible to invoke the Essence# run time system directly from your own application's C#, F# or VisualBasic code (bypassing even the DLR's hosting protocol.) If you do it that way, you can have full control over which class libraries are loaded in to an execution context (and when.) It also makes it possible to compile and run Essence# source code that doesn't reside on the local hard drive, but is perhaps generated by your application in RAM, retrieved from a DBMS, or obtained over the internet. And it makes it possible to store the results of compiling a "Do It" as a function (i.e., a CLR delegate,) which can then be invoked multiple times without recompiling.

Note that, because Essence# is fully virtualized, there can be multiple execution contexts active in parallel, each one completely isolated from the other, and having its own, private versions of each namespace, global variable, trait or class that's defined by "the same" class library. That's true even when the execution contexts are running in the same application domain.

Each separate invocation of the ES ("Essence# Script") tool runs all the scripts specified in its command-line argument in the same execution context; but that execution context is completely isolated from the execution contexts used by other invocations of ES, and from any and all other invocations of the Essence# run time system.

Defining a class library

Class libraries are stored in the file system. By default, shared class libraries (including the Standard Library) are located in the folder %EssenceSharpPath%\Source\Libraries, although that is not required. Any class library--including the Standard Library--can be located anywhere that is listed as a library search path by the set of configuration profiles that are currently active (see the section on configuration profiles for details.)

Structurally, a class library uses files and file folders to represent, declare, define, configure and initialize class libraries, namespaces, classes, traits, methods, global constants and global variables. Any folder that is immediately contained by the folder specified by the execution context as a class library path, and whose name qualifies as a qualified identifier, will be interpreted by the library loader as a class library. File folders directly or indirectly contained by a class library folder will be interpreted either as the declaration of a namespace, as the declaration of a class, or as the declaration of a trait, based on rules explained below (remember that a class is also a namespace, as is a trait.)

Namespace folders that are immediately contained by a library folder are interpreted as being declared in the root namespace. The file folder hierarchical containment relationships will be interpreted as the intended hierarchical containment structure of the namespaces/classes/traits in the class library. Note: The file system hierarchical structure is not interpreted as the class inheritance structure!

Each file folder contained in a class library folder (directly or indirectly) represents either a namespace, a trait, or a class--provided the folder's name qualifies as a legal Essence# (unqualified) identifier. The code that configures and initializes namespaces, classes, metaclasses, traits, global variables and global constants resides in files with special names, as does the code where methods are declared.

The file names that the library loader will accept and handle as participating in the library definition protocol are as follows, listed in the order in which they will be processed (any other files will simply be ignored). None of the files are required:

Note: The file names used by the library loader have been changed as of the "Philemon" release (Alpha Build 17.) This document uses the new names, but also lists the old ones. The new library loader will automatically rename files using the old naming scheme so that they will have the new names.
  • namespace.def: This file should contain Essence# code whose purpose is to configure the namespace that was declared by the folder that contains it. The namespace is configured by sending it messages. [Old names: configure.namespace, namespace.configure]
  • trait.def: This file should contain Essence# code whose purpose is to configure the trait that was declared by the folder that contains it. The trait is configured by sending it messages. The presence of this file in a folder causes the library loader to automatically interpret the containing folder as a trait declaration. [Old names: configure.trait or trait.configure]
  • classTrait.def: This file should contain Essence# code whose purpose is to configure the metatrait of the trait that was declared by the folder that contains it. The metatrait ("ClassTrait") is configured by sending it messages. The presence of this file in a folder causes the library loader to automatically interpret the containing folder as a trait declaration. [Old names: configure.classTrait or classTrait .configure]
  • class.def: This file should contain Essence# code whose purpose is to configure the class that was declared by the folder that contains it. The class is configured by sending it messages. The presence of this file in a folder causes the library loader to automatically interpret the containing folder as a class declaration. [Old names: configure.class or class.configure]
  • metaclass.def: This file should contain Essence# code whose purpose is to configure the metaclass of the class that was declared by the folder that contains it. The metaclass is configured by sending it messages. The presence of this file in a folder causes the library loader to automatically interpret the containing folder as a class declaration. [Old names: configure.metaclass or metaclass.configure]
  • methods.instance: This file should contain Essence# code whose purpose is to add methods to the class or trait that was declared by the folder that contains it. Methods are added to the class by sending messages to it. In the Standard Library, the methods defined by files of this type are expressed as method literals--but that is not required. The presence of this file in a folder causes the library loader to automatically interpret the containing folder as a class or trait declaration (it will be assumed to be a class, unless there is either a 'trait.def' or a 'classTrait.def' file in the same folder.) [Old name: instance.methods]
  • methods.class: This file should contain Essence# code whose purpose is to add methods to the metaclass/metatrait of the class or trait that was declared by the folder that contains it. Methods are added to the metaclass/metatrait by sending messages to it. In the Standard Library, the methods defined by files of this type are expressed as method literals--but that is not required. The presence of this file in a folder causes the library loader to automatically interpret the containing folder as a class or trait declaration (it will be assumed to be a class, unless there is either a 'trait.def' or a 'classTrait.def' file in the same folder.) [Old name: class.methods]
  • <identifier>.constant: Any file with the extension ".constant" will cause the library loader to declare a constant in the namespace (or class) represented by the folder containing the file. The name of the constant will be the prefix of the local filename for which ".constant" is the suffix. The text of the file should contain Essence# code that sends configuration/initialization messages to the BindingReference that will hold the value of the constant. The value of the constant will be the value of the final expression/statement in the file (or nil, if the file contains no code.) The library loader will make the BindingReference and its associated BindingHandle immutable in a later processing step.
  • <identifier>.variable: Any file with the extension ".variable" will cause the library loader to declare a variable in the namespace (or class) represented by the folder containing the file. The name of the variable will be the prefix of the local filename for which ".variable" is the suffix. The text of the file should contain Essence# code that sends configuration/initialization messages to the BindingReference that will hold the value of the variable. But the file may legally contain no text at all. The value of the constant will be the value of the final expression/statement in the file (or nil, if the file contains no code.)
  • initialize.class: OBSOLETE This file contains Essence# code whose purpose is to initialize the class that was declared by the folder that contains it. This file is no longer used. A file with a different name and a different format has replaced it. See below.
  • initialize.metaclass: OBSOLETE This file contains Essence# code whose purpose is to initialize the metaclass of the class that was declared by the folder that contains it. This file is no longer used. A file with a different name and a different format has replaced it. See below.
  • initializer: This file should contain executable code (a "Do It", with the same syntax as can be used in a block) whose purpose is to initialize the entity that is declared/defined by the folder that contains it (which may be a namespace, a class or a trait.) During the execution of the code in the initializer file, the pseudo-variable 'self' will be bound to the entity that is declared/defined by the folder that contains the file. The code of all initializer files is executed only after all the entities declared/defined in all loaded class libraries have been declared and defined, and after all methods have been compiled and added to their respective traits and classes. Other than that, there is no guarantee regarding the order in which initializers may be executed.
  • The difference between declaring, defining or configuring a namespace, class or trait and initializing a namespace, class or trait is that declaration, definition and configuration must happen before the methods of a class or trait can be correctly compiled, whereas initialization requires that the methods of a class or trait must have already been successfully compiled. There will be some "configuration" or "initialization" operations that don't care about the order of execution, and those can be placed either in the definition/configuration or in the initialization file, at the discretion of the programmer.

Class library file syntax and semantics

Some of the files listed above that participate in the class library definition protocol do not use the same syntax as is accepted and understood by the ES script execution tool. Instead, they use a restricted subset, which is nevertheless considered to be valid Essence# syntax, and which may be used in other contexts for other purposes, and most probably will be. There is even a name for that restricted syntax, which is self expressions (<- that's a clickable wikilink where the syntax of self expressions is defined and explained. in detail.)

But here's a brief explanation, specific to the use of self expressions by the library loader, which should be sufficient for those who are well-versed in the full, unrestricted Essence# syntax:
  1. A self expression is restricted to being a single expression, which should include at least one message send, but which may include any number of additional message sends by means of cascaded message syntax.
  1. The receiver of the message cannot be specified syntactically, but at runtime will be some object that will be provided programatically by the code that invokes the compiler. Conceptually, the message receiver will be a syntactically-missing but implied 'self'; hence the name self expression.

The configuration/declaration files participating in the class library definition protocol which use self expression syntax may optionally contain a single Essence# expression as their text, possibly involving multiple cascaded messages, with each message having the same (syntactically unspecified) receiver.

The files that use self expression syntax are all those that have ".def" as their file extension suffix, the "methods.instance" file and the "methods.class" file. All the other files use "executable code" syntax (the same as is used inside a block.)

Definition/configuration files (those whose names end with the suffix ".def.") will be executed by the library loader with the namespace, class, metaclass, trait or classTrait they are intended to define/configure as the syntactically-unspecified "self" (message receiver) value.

Method files (those named either "methods.instance" or "methods.class") will be executed by the library loader with the class, metaclass, trait or classTrait whose methods they are intended to declare as the syntactically-unspecified "self" (message receiver) value. In the Standard Library, the methods defined by files of this type are expressed as method literals--but that is not required.

Constant declaration files (those whose names end with the suffix ".constant" will be executed by the library loader with the BindingReference that implements their namespace binding as the value bound to the pseudo-variable "self" (which must be explicitly specified.) [These files used to use "self expression" syntax, but now use normal "Do It" syntax]

Variable declaration files (those whose names end with the suffix ".variable" will be executed by the library loader with the BindingReference that implements their namespace binding as the value bound to the pseudo-variable "self" (which must be explicitly specified.) [These files used to use "self expression" syntax, but now use normal "Do It" syntax]

Initialization files (which have the name "initializer") will be executed by the library loader with the namespace, class or trait they are intended to initialize as the value bound to the pseudo-variable "self" (which, unlike the case in files that use self expression syntax, must be explicitly specified.)


The essence of OOP: It's all messages, all the time.


Last edited Aug 10, 2014 at 10:22 PM by Strategesis, version 63