Ende

Ende is an esoteric programming language. It is a modern object-oriented language inspired by Glass and JavaScript that has a rich prototype-based object model and (un)intuitive queue-based semantics. Some of its concepts are also borrowed from the Sve programming language.

The basic idea of the language is that the execution environments (scopes) are first-class objects as in Sve. In fact, all objects can and must be used as environments in order to manipulate them.

Reasons to use this language
The concepts of this language are supposed to be painful but clearly possible to understand. These concepts include:


 * Queue semantics as opposed to traditional stacks. Queues are not necessary harder to understand than stacks, but they require some practicing. For example, the stack-based program  would be   in a queue-based language.
 * An object can be directly manipulated only if the object is the current environment object. Subenvironments are created when not needed. The prototype and parent hierarchies are complex and the programmer must be aware of them always.
 * The abuse of asynchonous methods and promises brings into light the best and worst sides of JavaScript.

The author thinks that these concepts are beautiful in theory, but in practice make programming irritating. They are concepts that should not be used in serious programming languages, although this has been done in smaller scale in for example JavaScript and Sve. The author invites anyone who disagrees to try programming using Ende.

Object model
Each object contains always:


 * a hash map from strings to objects
 * a queue of objects

Additionally an object can be primitive and contain other data. For example, Integer objects contain their integer value.

This can be summarized by the following pseudocode declaration:

class EndeObject { Map map; Queue queue; } class EndeInteger extends EndeObject { int value; } // other primitive types...

Magic keys
The hash map can contain the following special keys:

Prototype hierarchy
Ende is a prototype-based language. The prototype of each object is stored with the  key in the hash map. It is not required that each object has a prototype, but an object is useless without a prototype.

The objects form a tree of prototypes called the prototype hierarchy. At the top of the tree is, which is the only builtin object that has no prototype. Below that is, which contains all the basic methods required to manipulate the objects. All other objects should be below it, like  and   are.

If a key is not found in the hash map of an object, it will be searched from the prototype (and its prototype recursively if needed).

Evaluation model and the parent hierarchy
An Ende program is a list of commands, each which can be a method call, a string literal or an object literal. Every command is evaluated on an object, called the environment. At begining of the program the environment is the  object.

The simplest command, string literal, simply creates a new primitive String object and pushes it to the queue of the environment.

[this is string literal]

When a method call is evaluated, first the body of the method is searched from the hash map of the environment. Then a new subenvironment is created and the body is evaluated in that environment.

[the d method will print this string]d [a method could have a longer name in parentheses](this is a long method name)

When an object literal is evaluated, a new subenvironment is created and the code inside the literal is evaluated in that subenvironment. Then the subenvironment is pushed to the queue of the parent environment.

{[desc][this is the value of the "desc" key in the hash map. it is assigned using the = command]=}

A subenvironment will have its  and   keys assigned to its parent environment, that is, the environment where it was created. The parent objects form a tree called the parent hierarchy. All user-created objects will have a parent, while none of the builtin objects do.

All commands with the exception of,   and   modify only the queue of the current environment.

Method calls
Because the it is only possible to call methods of the current environment, the only way to access methods of other objects is to evaluate code on them using the  command.

For example, to concatenate to strings, the  method defined in the   prototype should be used. This can be done by pushing the second string to the queue of the first string object and then evaluating code containing the method call on the first string. Lastly, the result must be pulled from the queue of the first string to the original environment.

[Hello, ][World!]<>[.]<:&d [Hello, ]                 Push "Hello, " Queue: "Hello, " [World!]         Push "World!" Queue: "Hello, " "World!" <        Duplicate      Queue: "Hello, " "World!" "Hello, " >       Push to        Queue:                    "Hello, " [.]    Push "." Queue:                   "Hello, " "." <   Duplicate      Queue:                    "Hello, " "." "Hello, " :  Evaluate on    Queue:                                  "Hello, " & Pull from      Queue:                                            "Hello, World!" d Print

The  command is used to push   to the queue of. After the  method is called using  , the result is pulled from the queue using the  command.

Of course, it is not always necessary to evaluate code on the object itself. This is the case for example when the method will not modify the object and the object is not primitive. In these cases it might be easier to create a new subenvironment using the  command and change its prototype to the object. This way we can access the methods via the prototype. As the bodies of the methods will be evaluated in this subenvironment, they will not modify the original object, even if they try.

Classes and object initialization
There are no classes, only prototypes. In a way, the prototype is a class, but that is stretching. Is the prototype instance of itself? Are the subclasses instances of the classes they inherit? Are subenvironments instances of the objects/classes they inherit?

Anyway, to create a "class" it is enough to create an object that contains method definitions like this:

[Person]{ [__name__][Person]= [printName][[name]?d]= }=

The  field is used to set the name of the class. The class has one method,  that will print the string in the   key.

To create a person object, the  field of a new object must be set to be the Person prototype:

[suzie]{[Person][__proto__]?=[name][Suzie]=}=

The code will create a new object and assign it to the  field of the current object.

To call the, we can use either the   or the   command. Both of these will work, as the method does not modify the object.

[suzie]?[(printName)]: [[suzie][__proto__]?=(printName)]^

Control structures
The flow control is based on callbacks. The  command will create a promise that is successful only if the value it pulled was truthy.

For example, the following code tests if  and   are equal. Note that the methods of the promise must be called using  as they modify the internal state of the promise object.

[a][b]??<>[-]<:& i {}<>[,[They are not equal]d]<>[(then)]<: {}<>[,[They are equal]d]<>[(catch)]:

It first subtracts them and then creates the promise using. The promise will fail if they are equal, because  is falsy and other integers are truthy.

The only loop available in Ende is the for-each loop of the  prototype. For example, to print the numbers from 0 to 10, it is necessary to create a list of them using the  method and then call the  method of the list.

[$[d]f]:
 * 1) [10]<:(range)

Hello world
[Hello, world!]d

Cat program
{[List][__proto__]?=[0]#=}[$[<![#[1]<:+]<:&<=l{}<>[d]<>[(then)]:]f]: