The Ė programming language (logo)

Examples

Essentials

You might notice the lack of quotation marks on the arguments, which are the parenthesized parts of commands: Hello, world! (the only one in the first program), A, and B. This is because Ė does not make a distinction between names (variables) and literal values in the usual way. We will come back to this.

Otherwise, these two are self-explanatory:

run write(Hello world!)
Hello world!
run write(A) write(B)
AB

Loops ex machina

Here, we might want to imagine breaking up write(A) write(B) write(A) into two bigrams: write(A) write(B), and write(B) write(A). They independently describe the permitted courses of action for the program (here, two of them: “B after A“ and “A after B”), but do not otherwise interact, hence the loop:

run write(A) write(B) write(A)
ABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAB
Error: the program had requested more than 5 seconds of time for non-reading activity, and was therefore halted.

Use a modifying apostrophe ' to turn the write(A)s into to two distinct commands. This prevents the loop, but the write(A) commands are still identically behaved, by virtue of invoking the same action:

run write(A) write(B) write(A)'
ABA

Expecting failure

An action can result in success or failure.

Note that the standard library’s read() is unusually literal-minded. It must know in advance what has to be read:

run read(Something to be read.)
Something *else* to be read.
Error: all possible successors to run have been exhausted. The candidates were:
1)
read(Something to be read.)
All of them produced failing outcomes.
run write(Well, do you? ) read(yes) write(Cool! I won't tell anyone. :\))
Well, do you? yes
Cool! I won't tell anyone. :)
run write(Well, do you? ) read(yes) write(Cool! I won't tell anyone. :\))
Well, do you? in a way
Error: all possible successors to write(Well, do you? ) have been exhausted. The candidates were:
1)
read(yes)
All of them produced failing outcomes.

Reacting to different outcomes

Before tackling the next program, let’s reiterate the bigram-related semantics of Ė.

On each line, commands (each referring to an action) inevitably follow one another. Let’s say that a line contains X Y, two hypothetical commands, juxtaposed. Ė takes it to mean that anytime an X is done, following up with a Y is a permitted course of action.

In fact, the order of commands bears no other meaning than that.

For lines, the order is even less relevant. It serves merely to introduce priorities when two conflicting courses of action are possible.

Concretely, the program knows two ways of proceeding after a write(Well, do you? ). First, it tries read(yes) and fails, and because of that, it tries read(no) next.

run write(Well, do you? ) read(yes) write(Cool! I won't tell anyone. :\))
write(Well, do you? ) read(no) write(Dear diary, I guess an interrogator's job is just not for me.)
Well, do you? no
Dear diary, I guess an interrogator's job is just not for me.

To avoid typing write(Well, do you? ) or an even longer string all over again, as we did above, we can splice in another command, like -r.

Any command that starts with a hyphen (the default “dash” character) is perfectly valid, but does nothing on its own, in order to serve this “pivoting” purpose.

run write(Now that I'm no longer on duty as an interrogator... do you? ) -r
-r read(yes) write(Who doesn't, though?)
-r read(no) write(You might want to try it.)
Now that I'm no longer on duty as an interrogator... do you? no
You might want to try it.

Negotiation

Variables and literal values are not distinguished in Ė.

One can, but need not, also construe it this way: all word-like tokens are variables, but they come pre-initialized to themselves, that is, their names.

In the following, this pre-initialization is just passively maintained, which is why all tokens will behave as literal values:

run write(A number, please: ) read(3) write(3 might or might not be a number.)
A number, please: 3
3 might or might not be a number.
run write(A number, please: ) read(3) write(3 might or might not be a number.)
A number, please: 4
Error: all possible successors to write(A number, please: ) have been exhausted. The candidates were:
1)
read(3)
All of them produced failing outcomes.

Now, we place an asterisk before read(3), so that it spells *read(3).

This tells the read action not to fail early, but to “try harder”, in case negotiating the token 3 would allow it to succeed.

Negotiating 3 to XXI means that as the program proceeds, 3 ceases to mean 3, and starts meaning XXI instead:

run write(A number, please: ) *read(3) write(3 might or might not be a number.)
A number, please: XXI
XXI might or might not be a number.

In a way, everything is a variable here, or, rather, a negotiable.

However, new negotiations are never made if there is no asterisk. Also, to be negotiable, a token has to start with a capital letter, or to be numeric.

run write(A number, please: ) *read(Thing) write(This Thing thing, it might or might not be a number.)
A number, please: dog
This dog thing, it might or might not be a number.

A token is never negotiated to a non-token, such as a (space-separated) sequence of tokens:

run write(A number, please: ) *read(Thing) write(This Thing thing, it might or might not be a number.)
A number, please: a fierce pack of exactly one dog
Error: all possible successors to write(A number, please: ) have been exhausted. The candidates were:
1)
*read(Thing)
All of them produced failing outcomes.

When negotiable tokens have numbers in them (like Chapter1, Chapter2), arranging such tokens in pairs or longer sequences will exhibit some additional specifics of behavior. Specifically, that happens when the tokens are otherwise identical (as in Chapter, plus a number), except for the numbers (as in 1 and 2).

The sequences are automatically shortened or lengthened to make the negotiation possible, and the upper bound number (here: 3) is negotiated as well:

run write(What is love? ) *read(Word1 Word2 Word3) write(While "Word1 Word2 Word3" is correct, your time was already up. Sorry.)
What is love? a fierce pack of exactly one dog
While "a fierce pack of exactly one dog" is correct, your time was already up. Sorry.
run write(What is love? ) *read(Word1 Word2 Word3) write(You put it in 3 word(s). Try fewer.)
What is love? homoscedasticity
You put it in 1 word(s). Try fewer.

Grouping multiple tokens with balanced parentheses (), square brackets [] or curly brackets {} turns them into a single token, which has sub-tokens:

run write(Let's match some parentheses here and there: ) *read((here X) (there Y)) write(X + Y = ???)
Let's match some parentheses here and there: (here (be dragons)) (there there)
(be dragons) + there = ???

Standard library

The highly preliminary standard library can read, write, spell, split, pick, calculate, interpret, (check if the tokens in the argument are) all-equal, (check if a date/time string holds) now, (check if a number is) round. Note that if we add an asterisk, “checking if X” comes to mean “making necessary negotiations so that X”.

The examples below illustrate the behavior of some of the actions. First, spell and all-equal:

run write(Recite the alphabet: ) *read(Alphabet) *spell(Alphabet -> L1 L2 L3) all-equal(L1 L2 L3) write(We lost the original, so from now on it will be L1, repeated 3 times. Thank you.)
Recite the alphabet: BBBBBBB
We lost the original, so from now on it will be B, repeated 7 times. Thank you.
run write(Recite the alphabet: ) *read(Alphabet) *spell(Alphabet -> L1 L2 L3) all-equal(L1 L2 L3) write(We lost the original, so from now on it will be L1, repeated 3 times. Thank you.)
Recite the alphabet: ABCXYZ
Error: all possible successors to *spell(Alphabet -> L1 L2 L3) have been exhausted. The candidates were:
1)
all-equal(L1 L2 L3)
All of them produced failing outcomes.

If one need not report a number like the 7 in “repeated 7 times”, then the following also accomplishes the above task. It makes use of the fact that *spell(Alphabet -> L L L) cannot negotiate the repeated L tokens to different values.

Also, spell, as well as some other actions, will scan the tokens in their arguments left-to-right, oblivious to anything past the previous token, in the same way that commands are treated in an Ė program. Consequently, -> L L L means the same as, say, -> L L L L L.

run write(Recite the alphabet: ) *read(Alphabet) *spell(Alphabet -> L L L) write(We lost the original, so from now on it will be L, any number of identical copies of L, in any order. Thank you.)
Recite the alphabet: ZZZZZ
We lost the original, so from now on it will be Z, any number of identical copies of Z, in any order. Thank you.
run write(Recite the HIJKLMNOP thing from grade school: ) *read(ThingFromGradeSchool) *spell(ThingFromGradeSchool -> L1 L2 L3) *pick(L1 L2 L3 -> FavoriteLetter) write(Oh, the memories. I was particularly fond of FavoriteLetter.)
Recite the HIJKLMNOP thing from grade school: 1234567890
Oh, the memories. I was particularly fond of 8.

For completeness, we bring loops back into the picture, as well as introduce calculate:

run write(Will I make it back from the past? ) write(1900 ) *calculate(1900 + 1 -> 1900) write(1900 )
Will I make it back from the past? 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030
Error: the program had requested more than 5 seconds of time for non-reading activity, and was therefore halted.

Recall that -dash-commands are always valid, and always do nothing. This way, we can split up the program into lines easier:

run write(Will I make it back from the past? ) *now(2020) write(1950 ) -gonna-make-it
-gonna-make-it calculate(1950 < 2020) *calculate(1950 + 1 -> 1950) write(1950 )
-gonna-make-it write(I'm home!)
Will I make it back from the past? 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 I'm home!

It might be worth noting that now(2020) is the same as *now(2020), as long as it is still 2020. Past that date, only the “asterized” version will succeed, and will negotiate 2020 to actually mean the present year.

run write(How old are you? ) *read(7) *now(2020) *calculate(2020 - 7 = 2013) write(It's likely that you were born in 2013.)
How old are you? 88
It's likely that you were born in 1932.

And that is it. Thank you for your interest!

This is the Ė interpreter. (Open a blank interpreter window.)
Version 0.1.5 by Ignas Rudaitis, 2020-2021

Enter a program, then push Ctrl + Enter (or the preceding link) to run.
Use Escape to stop one that is running.