Logo
  • xqizit cloud
  • learn
  • get involved
get started
xqizit
xqizit

How to use Class Keywords in Ecstasy

icon

XTCLANG GUIDES

How to use class keywords in Ecstasy

xtclang classes class keywords interface mixin enum

Authored by: Abby Sassel, Cameron Purdy

Last edited: @February 10, 2024

icon

About this guide

This guide explores class keywords interface, mixin and enum. By the end of this guide, you'll have a clearer idea of when and how to use Interfaces, Mixins and Enumerations respectively in the Ecstasy language.

Topics:

  • Interfaces and super-interfaces
  • Examples of mixins to dynamically modify or extend functionality
  • Examples of enums to represent a fixed set of options

Target Audience

This guide is well-suited to programmers already familiar with classes, objects and inheritance, who'd like to learn more about advanced class declarations in Ecstasy.

If you'd like to understand more about the basics of Classes in Ecstasy first, we suggest you start with this How to define Classes in Ecstasy guide.  

Interfaces

Interface definitions in Ecstasy are similar to Java, using the extends keyword to specify super-interfaces, (equivalent to the use of : in C#).

Here is the List interface, for example:

interface List<Element>
        extends Collection<Element>
        extends UniformIndexed<Int, Element>
        extends Sliceable<Int>

The use of multiple extends clauses in the code above could be replaced with a comma-delimited list of super-interfaces; the two forms are equivalent:

interface List<Element> extends Collection<Element>,
        UniformIndexed<Int, Element>, Sliceable<Int>

Mixins

Mixins allow classes to incorporate and compose functionality or attributes from other classes with more flexibility than traditional inheritance. Here is an example declaration of a mixin, ListFreezer, whose job it is to add the ability to freeze (i.e. "make something immutable") to any List:

mixin ListFreezer<Element extends Shareable>
        into List<Element> + CopyableCollection<Element>
        implements Freezable

It's important to clarify that this mixin isn't applicable to just any List.It only applies to a List class that has an Element type that is Shareable, and the into clause specifies that the mixin can only be applied to a List class that is also a CopyableCollection. This is how a mixin defines its constraints on what classes it can be incorporated into. As you can see, though, the mixin is itself a form of class, specifying that it implements the Freezable interface, for example. So when the mixin is incorporated into a List, the resulting List class will automatically pick up those additional building blocks, such as implementing the Freezable interface. For now, don't focus on how this happens; we'll discuss those details in a subsequent chapter.

Here's another mixin, ListMapIndex, which is used to add a fast-lookup index to a ListMap. Look at its into clause and its type parameter declaration — it specifies that it can only be mixed into a ListMap, and more specifically, into one whose Key type is Hashable:

mixin ListMapIndex<Key extends Hashable, Value>
        into ListMap<Key, Value>

Now, let's see how this mixin is used in the ListMap class:

class ListMap<Key, Value>
        implements Map<Key, Value>
        implements Replicable
        incorporates CopyableMap.ReplicableCopier<Key, Value>
        incorporates conditional ListMapIndex<Key extends immutable Hashable, Value>
        incorporates conditional MapFreezer<Key extends immutable Object, Value extends Shareable>

This example is more complex than the previous ones. Note that ListMap does not require that its Key be Hashable, yet it incorporates ListMapIndex, which as we just noted requires its Key to be Hashable! So let's examine that incorporates line closely:

incorporates conditional ListMapIndex<Key extends immutable Hashable, Value>

The conditional keyword in ListMap essentially means: "Incorporate the ListMapIndex mixin if-and-only-if my Key type is an immutable Hashable." Pause for a moment to think about what is happening here: It is as if the class is being automatically altered based on the type of Key that the ListMap is instantiated with!

There is one more way to specify a building block, which is the delegates clause, seen here in the const ChickenOrEggMapping class, shown here in full:

const ChickenOrEggMapping<Serializable>(function Mapping<Serializable>() egg)
        delegates Mapping<Serializable>(chicken) {

    private function Mapping<Serializable>() egg;

    private @Lazy Mapping<Serializable> chicken.calc() {
        return egg();
    }
}

The "egg" function is provided as part of the constructor, and then the "chicken" is lazily created the first time that it is needed -- by calling the "egg" function and caching the resulting "chicken". What the delegates clause does, though, is incredible: It automatically redirects all of the calls coming into the ChickenOrEggMapping, and it will send them to the newly hatched chicken. In other words, the delegates clause implements an entire interface by routing all of the interface methods to another object -- and it does all that in one simple and obvious line of code. By delegating all of the Mapping calls to the chicken, the ChickenOrEggMapping acts just like a chicken, even though it was constructed with only an egg. And by delegating to a @Lazy property, the first such delegating call to come in will automatically cause the chicken to be hatched from that egg!

Enumerations

Enumerations, or enums, allow us to define named constants representing a fixed set of options. Their syntax resembles Java's enum. For example:

enum Event {Created, Modified, Deleted}

In this example, the Event class is compiled as an @Abstract const class, and the class object for the enumeration implements the Enumeration interface, allowing easy programmatic access to the values of the enumeration, such as iterating through all of the enumeration's values:

for (Event event : Event.values) {
    console.print($"event={event}");
}

Each enumeration value is a singleton const class that extends the enumeration class and implements the Enum interface; in the example, Created is a singleton const that extends Event. This allows for simple and obvious usage of enumerations, such as:

Event event = Created;

And:

if (event != Deleted) {
    process(event);
}

We hope this guide has shown you how to define and work with interface, mixin and enum class keywords in Ecstasy for more idiomatic and sophisticated program control.

icon

EXPLORE & LEARN

Ready to learn more?

Check out our latest guides, blogs and tutorials!

Take me to the latest!

Logo

Team and contributors

Code of conduct

GitHubYouTubeLinkedInMastodonX