Tense

From Esolang
Jump to navigation Jump to search

Tense is an esoteric programming language is an unimplemented concept for a programming language based on temporal logic. That is, the programmer reasons not only about the current state of the program, but also what states the program was in previously, as well as making assertions about the potential future state of the program (within the boundaries of what is computable with a Turing Machine; i.e. undecidable statements like the halting problem are still impossible). In essence, it can be thought of as the complete antithesis to functional programming, which aims to remove the notion of state as much as possible; meanwhile, Tense embraces it.

The name "Tense" is a reference to the concept of tense in languages, such as the past, present, and future tense.

Philosophy

Reasoning about time is ubiquitous in our everyday life. Even if it is no longer true that a state holds in the current time, it may still be important to know that an entity was in that state at some point in the past. For example, a person applying for an insurance policy after having changed jobs may still need to provide information about their former workplace in order to receive benefits. A property owner may evaluate the appraisal of a home based on not only its current, but also past conditions, even if said conditions are no longer true - this is typically known as a historical or retrospective appraisal.

In many areas of science, systems are often dependent on not only the variables in the current moment, but also on what state the system was in before; this is known as hysteresis. Hysteresis can be observed in phenomena such as ferromagnetism, as well as deformation of elastic materials such as rubber bands tied to weights following a prolonged period of time. These systems benefit greatly from temporal logic modeling.

Temporal logic has been used as a paradigm for domain-specific languages, particularly formal specification languages such as TLA+, which are used to verify the correctness of programs. However, attempts at implementing temporal logic in mainstream, low-level systems programming languages are not widely known. That being said, some papers have been written on the subject which argue for various approaches, notable proposals include TEMPLOG (Abadi & Manna, 1989) and TeDiLog (Gaintzarain & Lucio, 2009).

Syntax

The syntax of Tense is similar to languages like C or Java. The most notable addition is the keyword recordable, which is appended to the beginning of a variable declaration, much like const or volatile:

recordable int value = 3;

Declaring a variable as recordable means that the program will continuously save the state of the variable (by default every 0.1 seconds, which can be changed by the programmer) for as long as the variable is alive, and stop saving after the variable is destroyed (unless it is declared static, in which case the variable is never destroyed, and thus the state is recorded until the program ends). These states are saved into a map (dictionary for Python users), known as a timelist, which maps timestamp keys to the values of the variable at those specific timestamps.

Small sample of values from an example timelist
Timestamp (Key) Value
1.0 3 (variable created)
1.1 3
1.2 3
1.3 3
1.3 < t < 5.0 3
5.0 7
5.1 7
5.2 7
5.3 7

In the table above, each timestamp is represented as the number of seconds since the program began. Here, the variable storing the value 3 was created 1 second into the program's life. The values are recorded at a rate of one recording per 0.1 seconds. The value of the variable is held at 3 until 5 seconds into the program's runtime, after which the value has been changed to 7. We don't care about what caused the value to change, just that it did. Importantly, all of the information about the variable having had the state 3 is retained even after the value has changed to 7. Thus, we can use this historical data to temporally reason about the variable's past state.

Why explicitly declare variables as recordable when all of them could be? Simply put, it saves on memory and performance; recording the state of a variable requires frequent additional processing for writing to the timelist, and the longer a program runs, the larger the timelist becomes. By requiring the programmer to explicitly define which variables are recordable, any variables the programmer does not need to reason about temporally can be left unrecorded to save on processing power and space. Unrecorded variables are also known as closed variables, while ones that are recorded may also be referred to as open; the analogy being that a closed variable is like a history book that has a permanently closed cover that can't ever be opened, reflecting its lack of a timelist.

Only recordable variables may be used in conjunction with temporal operators or keywords, as their past states are obviously required to reason about them in a temporal way. Attempting to use a temporal operator with a closed variable will result in a compile error.

The "wait" keyword

In most mainstream languages, halting execution of a thread/program for a set period of time is typically delegated to standard libraries and implemented as a function, usually called "sleep()". In Tense, delays are directly implemented as part of the core language specification, via the wait keyword. The exact syntax is "wait n", where n is a float value representing a duration in seconds.

recordable bool value = true;
wait 2.0;
value = false;

In the above snippet, value is initialized to true, and execution halts when it reaches the wait keyword. This lasts for 2 seconds, after which the program resumes and value becomes false.

The "was" keyword

The was keyword can be prefixed to the relational operators <, >, <=, >=, ==, and !=. It is one of the most important keywords in Tense. When appended to a relational operator in conjunction with a recordable variable, the program does not look at the current state of the variable, but instead looks at its previous states, via the variable's corresponding timelist, to see if the relational operator could be true for any of the values in the timelist. More formally, "X was <operator> N" is true if and only if there exists a value A in the timelist T, such that "X <operator> A" is true.

recordable int value = 3;
wait 2.5;
value = 7;
if (value was == 3)
{
  print("Value was 3.")
}

Here, the integer variable value is initialized to 3, a delay of 2.5 seconds is introduced, and afterwards value is changed to 7. The program then evaluates the if statement, and sees the "was" keyword before the == operator. The programs examines the timelist of value, and sees that it contains the value 3, so the if statement evaluates to true, and the print statement executes. If "was" were to be removed, this would evaluate as false.

Note that "was" does not care at all about the present value of the variable, only the past values in its timelist. If value was still 3 when the if statement was executed, it would still evaluate to true. That is, if either the condition was true in the past OR the present, then the statement evaluates as true. It may be desirable, however, to check against the current value of the variable as well; perhaps we want to check that while the condition was true previously, we don't want the condition to be true at the current moment. To do this, simply rewrite as follows:

recordable int value = 3;
wait 2.5;
value = 7;
if (value was == 3 && value != 3)
{
  print("Value was 3, but is no longer 3.")
}

This statement will evaluate was == 3 as true, just like before, but now also evaluates the current state of the variable with the present-tense relational operator !=, which evaluates to true too. Now, if value remained the same by the time the if statement was reached, the statement would evaluate as false, not true, and the print statement would not execute.

The "has been" keyword

Todo

The ~> (change) operator

Todo

The "when" keyword

Todo

References