Skip to content
Cameron Purdy edited this page Jan 20, 2022 · 14 revisions

Foreword

When is the last time that you learned a new language? I mean, legitimately and truly learned a new language, in detail, and in depth?

It's not an easy undertaking for someone to learn a new language. Many of the words used to describe the new language will look and sound like words that you already know, but now each word seems to get a slightly different meaning, and some words contain surprising twists that seem incongruent with the meanings that you are already comfortable with. There may exist an entirely different set of assumptions about what words mean, how things work, what styles should be embraced, and what programming approaches should be avoided.

It's this seemingly enormous chasm -- or perhaps, this minor crack in the sidewalk -- that this guide will help you safely cross. It will make the unfamiliar seem simple and natural, and the new and different seem simultaneously obvious and necessary. That's a tall order, and if you think of anything along the way that can make the process simpler and clearer, please let us know on our discussion forum. The same goes for questions that you might have, or edits that we should incorporate.

And now, without further ado ...

Introduction to the Ecstasy Language

Ecstasy is a programming language that will seem very familiar in many ways to any programmer who knows Java, C#, or C++. Its syntax design is intentionally similar to these languages, with bits and pieces influenced by other languages in the same general family, including Kotlin, Ceylon, Scala, and even Python. We wanted the syntax to feel familiar, like a well-worn glove. And we wanted it to be a joy to pick up as a new language, both in its familiarity, and also in its thoughtful innovations.

To illustrate this sense of familiarity, we're pretty sure that you won't need a detailed explanation to grok the basics of this Ecstasy code:

Boolean done     = False;
Int     count    = 0;
String  greeting = "hello";

And while there may be a few details that raise questions, there is probably nothing baffling about the Ecstasy "Hello World" example, either:

module HelloWorld
    {
    @Inject Console console;
    void run()
        {
        console.println("Hello World!");
        }
    }

Yet this simple familiarity also hides some fundamental differences in the underlying model of execution that the Ecstasy language was designed to support. It is that model that we will attempt to briefly describe here, before we proceed to the easy aspects of language syntax and structure. This fundamental model is important to lay out up front, because understanding it will make almost everything else seem secondary and inevitable -- in a word, obvious.

We're going to fly through these topics, laying out statements of fact without many supporting details. We promise that each of these topics will be examined in depth later in this language guide, but for now, try to accept these statements as axioms of the language -- even if these points temporarily raise more questions than they answer.

Containers

All execution of Ecstasy code occurs within an Ecstasy container; if Ecstasy code is running, it is running in an Ecstasy container.

An Ecstasy container has a type system; all Ecstasy code running in a container is defined by and is part of that type system.

Ecstasy containers are arranged hierarchically, like directories in a file system. But unlike a file system, it is not possible to navigate from a nested container to its parent container; a parent container is invisible and inaccessible to any of the containers nested within it. Nesting is recursive, and can be arbitrarily deep.

Type systems are both closed and immutable. Closed means that there are no dangling pointers; it is illegal for a type system to not be fully consistent with itself, with all type compositions verifiable by the rules of the XVM. Immutable means that new code cannot be introduced within a type system on the fly; once a container is created, its type system is guaranteed to be immutable.

New type systems can be created on the fly, and new nested containers can be created on the fly using those new type systems. As a result, it is possible to safely introduce new code, on the fly, into a running system -- but only in the form of a new container.

The deliberate safety of the container model is the result of the new container having no access to its parent container, or to its runtime environment, including the machine, the OS, the network, the local storage, and so on, unless that access is explicitly injected into the new container by its parent container. Furthermore, all access injected into a container is injected by interface, such that no other reflective details exist.

Ecstasy does not have a Foreign Function Interface (FFI); this was a careful and purposeful decision. No native code can exist inside of an Ecstasy container. All potentially-native operations, including all OS capabilities, can only be represented within a container by injected interfaces.

Security is the fundamental principle of the Ecstasy container model.

Type System

A type system is composed from modules.

Ecstasy is class based; all objects are of a class, and all classes come from modules.

In Ecstasy, everything is an object, and everything has a type.

The Ecstasy language provides a core "ecstasy" module, which is automatically present in every type system. This module contains all of the fundamental classes defined by the Ecstasy language, including integers, booleans, strings, arrays, and so on. Even the most basic types in Ecstasy are classes, and even the most basic values are objects.

Immutability

Objects can be immutable. Immutable state cannot be modified.

An object can be instantiated immutable, or an object can be made immutable. Once an object is immutable, it cannot be made mutable again.

Immutability is an explicit part of the type system. A type knows if it is explicitly immutable. An object can be tested to determine if it is immutable.

Services

Each service belongs to a container.

All execution of Ecstasy code occurs within an Ecstasy service; if Ecstasy code is running, it is running in an Ecstasy service in an Ecstasy container.

Each container is a service.

Each service is an object, and has a class.

Any object passed into a service from another service goes through a service boundary. The only objects that can go through a service boundary are either (i) other services, or (ii) immutable objects.

When a reference to a service object is passed through a service boundary, the object that appears inside the target service boundary is a service proxy. Any call made to a service proxy will go back through the service boundary to the service represented by the proxy.

With respect to all other services, each service executes asynchronously, concurrently, and when possible, in parallel.

When a call is made to a service proxy, the service represented by the proxy creates a new fiber to execute the incoming call.

All execution of Ecstasy code occurs within a fiber within an Ecstasy service; if Ecstasy code is running, it is running on a fiber in an Ecstasy service in an Ecstasy container.

Within a given service, only one fiber is permitted to be actively executing at a time.

A service represents a mutable domain of state. Mutable information can neither leave nor enter a service. All access to state and all mutation of state can only occur within the service that owns that state. Any attempt to access or modify state of a different service occurs by proxy.

Moving on ...

That list of axioms may seem overwhelming at first, but the design will become increasingly obvious as each new concept is explained. Don't be afraid to come back and re-read this section from time to time as you work through the subsequent chapters.

Next: Creating your first module