Entfedern
Entfedern is a programming language created by User:Deschutron in 2009, designed to be a prototyped language in which every variable assignment affects the original value of the variable. It was inspired by User:ais523's putative language Feather.
Introduction
When your program starts, the system says "Execution complete".
The storyline of this programming language, is that your program has just finished executing, and now the finalize method is being called in order to clean up after it. However, the output of the program was not what you intended. Now, by writing retroactive commands into the finalize method, you have your last chance to correct your program's behaviour. This is your program's chance to correct its mistakes.
Your program actually begins execution in a method called finalize. As an extra bit of fun, the finalize method does not actually return. The only way it can terminate is by causing a contradiction that wipes it from reality. So in a way, the program never succeeds. When the program terminates, the system says "Ready to begin".
This language is only Turing complete when it has errors.
program structure
Alice { // variable declarations // helper methods finalize() { // local declarations // retroactive imperative statements } }
The syntax is based on Java syntax, but is not completely the same. The helper methods may come before or after finalize(). Alice is the name of an object with the class structure described in the rest of the code. The only way you can create new objects is by cloning Alice. I think this makes it a prototyped language.
Example 1
Hello { String hello; finalize() { hello = "Hello, world!\n"; printed(hello); hello = "Goodbye"; } }
Output
with debug statements:
Execution complete Hello, world! Contradiction in Hello! Hello is sealed from time. Ready to begin
Without debug statements:
Hello, world!
All methods and verb-keywords are in past tense, except the declaration of finalize. finalize is supposed to be the only method being called in the present. To call finalize, you must refer to it as finalized(). Ostensibly you are saying it was called in the past. '=' is to be pronouced "equalled" or "was".
Some built-in methods, and keywords
spawned X creates a clone of the current object called X read() reads a byte from input and returns it printed(x) prints the variable x to screen finalized() calls the finalize() method sign(x) returns the mathematical sign of a number {-1, 0, 1}
Banned constructs
- while loops
- for loops
- if statements
- try blocks
- comparsion operators (==, != , <, >)
Data types
- all of Java's primitive types
- String
- the type of the defined prototype
operators allowed
+ - * / = (maybe some more)
Time-sealing
Entfedern behaves like a declarative language. You cannot change the value of a variable, you can only say what it was originally. Therefore all assignment statements must be logically-compatible as equality statements. If the program ever sets a variable to a value that contradicts a previous assignment to that variable, the object that contains that variable is in an indeterminate state, and so must be sealed from time completely in order to protect the timeline.
When an object is "sealed from time", it's execution ceases, and no other objects may access it. If the state of any other object is made to depend on the state of the time-sealed object, the other object is time-sealed as well.
Memory-wise, when an object is time-sealed, it's memory may be freed, because nothing will access its contents again.
The finalize method hangs when it reaches its end. To terminate it, you must time-seal the object that it is run from.
Example 2
Cat { int c; finalize() { int d; finalized(); c = read(); d = sign(c + 1); d = 1; printed(c); } }
This program's finalize is a recursive method. Assuming it has no errors, it is infinitely recursive. Therefore if it has no errors, it will run forever.
finalized() calls itself every time. In order to execute this loop, it is recommended that the compiled program, or interpreter, start with the hypothetical deepest level of recursion, and begin execution from there. This explanation assume this happens. The output stated by this explanation must be honoured no matter how the language implementation works.
The deepest level of finalized() is executed. In there, a byte is read from input and assigned to c. Then d is set to the sign of c + 1. Then d is asserted to be 1. Because d is fully defined by the previous statement, this line either has no effect, or causes an error. If this line contradicts the previous assignent to d, this line causes its object Cat to be sealed from time for its transgression. Otherwise, the flow turns to the next line. On the next line, c is printed to output. I haven't fully worked out what format things will be printed in, or how to control which format they are printed in. Then control is returned to the second deepest call. If any call encounters a contradiction, the object is sealed from time, and this terminates execution.
EOF is -1. When EOF is read, d is set to zero. sign(-1 + 1) = 0. For any value from 0 to 255, sign(c + 1) = 1. So the statement "d = 1;" only seals Cat from time when EOF is read. The program cats relays a file of input, like the cat command from Unix.
Reproducing Brainfuck behaviour
Here is a list of Brainfuck instructions and their equivalents in Entfedern:
, x = read(); . printed(x); < <recur>(pos - 1); > <recur>(post + 1); [ spawned Bob; Bob.foo(); inside Bob.foo(): y = sign(x)*sign(x); y = 1; ] close the method Bob.foo()
where x is the current position in memory.
About iterating through memory
Iterating through memory like a Brainfuck program is not straightforward, but is possible. What you need is an array to iterate through, and a recursive method.
Example 3
Bob { int[] a; sang(int note) { a[note] = 0; Bob.a[note] = read(); sang(note + 1); } danced(int number) { printed(a[number]); danced(number + 1); } finalize() { a.length = 27; // This program will read exactly 27 bytes spawned Charlie; Charlie.sang(); // Charlie reads into a, and then seals himself from time spawned Denise; // Denise has a copy of a with the read values in it Denise.danced(); // Denise writes the bytes Charlie = Denise; // Bob seals himself from time } }
This program performs read() 27 times, and writes the result to output.
Accessing an array outside of its bounds contradicts an assignment to its length. Offenders are in an indeterminate state and so are sealed from time.
After reading via Charlie, Bob spawns a clone called Denise and gets her to print. This is not necessary; Bob could have danced() himself, but I thought it was more romantic to refer to both the singer and the dancer in third person. When their song and dance is over, Charlie and Denise are both sealed from time. Any object that allows the state of a time-sealed variable to affect anything gets itself sealed from time, because its behaviour is undefined. So the final statement seals Bob from time, terminating the program.
sang() and danced() recurse at the end of themselves, so execution starts with the shallowest call. Each new method call is passed a new array index, and therefore has the ability to write to memory without contradicting a prior statement.
This program successfully iterates through memory.
Partial definition of variables
- Variables may be partially defined.
- You must define a variable with assignment operators, like in a normal imperative programming language.
You can't say
x = 5; didStuff(); x != 5;
because x != 5
is invalid as an imperative statement.
However, the following is possible:
x = 5; didStuff(); y = sign(x - 5) * sign(x - 5); y = 1;
As for partial definition of variables, you can do this:
int c; int x; x = c + 1;
At this stage, x is defined to be c + 1, and c is completely undefined. x is partially defined.
You can time-seal the program with this:
x = c - 1;
If you try this:
x = 0;
The program doesn't get time-sealed. Instead, c is set to -1. A corollary of this is that you can assign a value to a variable without putting it on the left side of the assignment operator.
Because of this behaviour, and the general treatment of contradictions, I suspect Entfedern is a declarative programming language.
Also, this language does type checking at compile-time, so don't think you can use something like this to end a program:
int x; x = read(); printed(x); x = "Hello!";
When it is interpreted, I don't know how to prevent the program from delivering its output because of this error.
If that is not possible, recommended behaviour for the interpreter is to print a warning to the screen, set x to a random value, and let execution continue.
A possible warning message is:
You dare to assign from an incompatible type? Your variable has been randomised.
Printing partially defined variables
A printed() call on a partially defined variable waits in the background for the variable to be completely defined, and then prints it. The printed() call is aborted if the program is sealed from time before the variable is defined.
Easter egg variables
The following variable names have special behaviour:
time
,Time
timeSealing
,TimeSealing
To use any of these variables, you must declare it as if it was a normal variable, or create an object with the name.
The difference comes when you seal them from time. When sealed from time, these variables have the following behaviours:
time
The system prints to standard output
Time is sealed from time. Now, nothing has ever happened, and nothing will happen. Only this moment ever exists.
The program then halts execution and hangs.
timeSealing
The system prints to standard output
Time sealing is sealed from time. Everything is true simultaneously. This is a contradiction. You do not exist.
The program then halts execution and hangs.