Browse by type
Naming is hard. Let's see how far can we go without.
This is a programming language based on three paradigms:
The main feature of the language is its avoidance of any naming of any sort. True to this maxim, the language itself doesn't have a name. "The namingless programming language" is a definition.
Since there is only one such language in existence, it doesn't need a name.
Fun. Mostly fun. This is a language for recreational programming.
Well, of course, you can also use it as a teaching tool to showcase tacit, stack-oriented, or array programming. Or use it for punishment in a BDSM play. I don't judge.
Like this:
i_^_b_H_i_cpp^_)_V_b_v_J_^_E_H_leafL_==^_)_V_H_Z_Z_^_)_V_H_I_^_E_1^_2^_#_G_Z_Z_^_E_1^_2^_#_H_$_L_-^_G_m_G_&_&_
Yes.
Sorry.
There is one and only one data structure. Since there is only one, it doesn't need a name.
So the data structure is a tree of chars. Every node is either a leaf containing a char, or a branch containing a dynamic array of the data structures. The array may also be empty. An empty branch is still a branch.
A string is a branch where every subbranch is a leaf.
A number is a string. Yes. The language supports decimal arithmetics on strings. Go COBOL!
An array is a branch where every subbranch is a string.
A matrix is a branch where every subbranch is an array.
Please note that none of these terms are names for anything language-specific. They are widely known data structures that are implementable in the language's data structure.
There is also the operation. Not "an operation" but "the operation" since there is only one operation in the language. Since there is only one, it doesn't need a name either.
The operation is spelled _ and it takes the last element from the branch before it and acts accordingly. So essentially the semantics of the operation is set by the pair of a prefix - a symbol before the operation - and the operation itself.
E. g. to add 2 and 2, you need to:
put a char 2 as a string. The pair for that is ^ and _.
2^_
then you need another 2 as a string. You can do the same 2^_ thing but you can also duplicate the last element in the current branch with a H and _ pair.
H_
finally, you need to perform the addition itself. That's easy, that's + and _.
+_
So the final program will look like this:
2^_H_+_
And when run, it will result in:
4
To avoid the naming problem for the programs you write, the language uses the name of the executable as the source code.
Let that sink in.
You get the executable interpreter for the language and rename it with the valid code in the language. This way, the file you run doesn't have a name per se but the program itself is the name of the executable you run.
So to run the scary program from above you do this:
./build.sh
mv the_namingless_programming_language i_^_b_H_i_cpp^_)_V_b_v_J_^_E_H_leafL_==^_)_V_H_Z_Z_^_)_V_H_I_^_E_1^_2^_#_G_Z_Z_^_E_1^_2^_#_H_$_L_-^_G_m_G_&_&_
./i_^_b_H_i_cpp^_)_V_b_v_J_^_E_H_leafL_==^_)_V_H_Z_Z_^_)_V_H_I_^_E_1^_2^_#_G_Z_Z_^_E_1^_2^_#_H_$_L_-^_G_m_G_&_&_
Of course, you can just copy and tweak the program you already have. In fact, that's what we normally do with Python, shell, or Perl scripts anyway.
Here is the full list of the operation pair prefixes.
By the way, the list is automatically generated from the source code by the i_^_b_H_i_cpp^_)_V_b_v_J_^_E_H_leafL_==^_)_V_H_Z_Z_^_)_V_H_I_^_E_1^_2^_#_G_Z_Z_^_E_1^_2^_#_H_$_L_-^_G_m_G_&_&_.
Since the source code for the language comes from the name of the executable, and on Windows, a dot separates the file name from its extension, the dot was chosen as a prefix that stops the execution. You can also use it to separate code from comments like this:
i_^_b_H_i_cpp^_)_V_b_v_J_^_E_H_leafL_==^_)_V_H_Z_Z_^_)_V_H_I_^_E_1^_2^_#_G_Z_Z_^_E_1^_2^_#_H_$_L_-^_G_m_G_&_&_._list of operation prefixes
Since _ is the command, you can't use it in arbitrary strings as it is. So the escape for the underscore is U_.
"U" has been chosen since it kind of contains the underscore in its bottom part. This is the recurring pattern. A grapheme that substitutes a symbol is usually a symbol with something else.
You can't usually use slashes in the file names. And even when you can it wouldn't be a good idea. Trust me. So there is an escape pair that puts / into the current data structure, and it is Z_.
Like with "U", "Z" is a slash plus two extra lines.
By the same logic, the backslash is a N_ pair. "\" plus two extra lines.
"J" looks like a bit like a return sign "⏎". So the pair for the line break is J_.
Since a dot is used as a name-extension separator on Windows, we can use i_ as a dot substitute just not to mess with the way file managers prefer to show stuff.
Although there is nothing wrong with putting space in the file name, sometimes it makes routine UI operations like copying things harder so we can avoid spaces by L_ substitution.
"L" is a horizontal line representing the space itself, and a vertical line that serves no purpose.
I_ stands for the single quote. You probably wouldn't get it until you see the double-quote pair.
Y_ is the double quote. "I" and "Y", single and double. Does it make sense now?
I see your confusion. This prefix doesn't have a coherent name. Well, naming is hard, that's why I started the whole thing to begin with.
What it does usually is it turns a piece of string into a subbranch containing the piece of string.
E. g. you want to add 12 and 23 together. In a normal language, you have literals, syntaxis, and operators, so 12 + 23 is the common way to do that. In this language, you only have a string of chars as input, so to add two numbers together, you need to segregate one from another first.
When you enter 12, you only enter char 1 followed by char 2. Your current branch contains two leaf elements.
leaf(1) leaf(2)
Now you elevate all the elements of the same rank making these two into a subbranch with two leaves inside.
12^_
...results in...
branch(leaf(1) leaf(2))
Now you add two more chars:
12^_34
branch(leaf(1) leaf(2)) leaf(3) leaf(4)
And then you elevate the last elements until they are of the same rank resulting in this:
12^_34^_
branch(leaf(1) leaf(2)) branch(leaf(3) leaf(4))
And only now, when you have two branches with what are essentially strings inside, you can use another operation pair to add them up.
12^_34^_+_
This now results in a new branch containing the following:
branch(leaf(4) leaf(6))
This outputs to a tab and 46. You can deelevate it back with the v_ pair, and then you'll get 46 without any tabs, but that's a completely different story.
This is something like [] in normal languages. A way to access an array.
Let's say our branch is filled like this:
leaf(1) leaf(2) leaf(4) leaf(8)
Since we might even know how many leaves there are, we start counting indices from the end. And for... reasons, we also start from 0.
So doing 2^_|_ to our subbranch picks the third element from the right, and copies it to the rightmost position in the branch.
leaf(1) leaf(2) leaf(4) leaf(8) leaf(2)
This is a way to access all the elements in a matrix, or a tensor, or a tree that lies on a certain depth, and in a certain position too.
Take this program for example:
1^_2^_3^_^_4^_5^_6^_^_^_2^_2^_#_
First, we elevate 3 chars so they become strings (or in terms of the language, branches that only contain leaves).
branch(leaf(1)) branch(leaf(2)) branch(leaf(3))
Then we elevate the strings, yes, all three of them, so they now constitute an array of strings (or, in terms of the language again, a branch where all the branches are the branches that only contain leaves).
branch(branch(leaf(1)) branch(leaf(2)) branch(leaf(3)))
Then we add three more strings and elevate them as well.
branch(branch(leaf(1)) branch(leaf(2)) branch(leaf(3))) branch(branch(leaf(4)) branch(leaf(5)) branch(leaf(6)))
Then we evaluate the two branches making a matrix of strings (or in terms of the language, oh boy, a branch that contains several branches with an equal number of branches that only contain leaves each).
branch(branch(branch(leaf(1)) branch(leaf(2)) branch(leaf(3))) branch(branch(leaf(4)) branch(leaf(5)) branch(leaf(6))))
Or in more conventional notation:
1 2 3
4 5 6
Now we do this:
2^2^#_
The first 2 is the index. And it also enumerates elements starting from 0, so 2 actually means "the third".
The second 2 is the depth. Also starting from 0. 2 and 2 means that we want the third element from the branch on the third level of embededness. That's leaf(3) and leaf(6) but also packed in a branch so it's more like branch(leaf(3) leaf(6)).
This operation prefix may also pick from the 0-th deepness level so, for instance, this program:
1^_2^_3^_2^_0^_#_
Results in 3.
Well, that's easy. If there is an element in the current branch, it can be multiplicated. For example, 3 times by doing 3^_m_:
test^_3^_m_
results in:
test
test
test
This is the abbreviated version of replication. It always adds exactly 1 copy of the last element.
test^_H_
results in:
test
test
The last element can also be easily removed.
1^_2^_3^_X_
results in:
1
2
Or swapped with the previous element.
1^_2^_3^_G_
results in:
1
3
2
Sometimes you need an empty string, an empty array, or an empty matrix. You can't elevate an empty element with ^_ because it elevates all the consequent elements of the same rank starting from the last one. So there is a dedicated prefix that elevates an empty element out of the blue.
A_
results in
branch()
This simply returns the amount of subbranches in the last element of the current branch.
1^_2^_3^_^_$_
results in:
3
Just like several subbranches can be elevated into one branch containing them all, the last element in the branch can be deelevated, putting all its contents in the current branch.
1^_2^_3^_^_v_
results, unsurprisingly, in:
1
2
3
The symbol pairs the grapheme for the "elevate" by the way. Isn't that sweet?
I love floating-point numbers. I wrote several tutorials on them:
-Yet another floating-point tutorial
-Estimating floating-point error the easy way
-Why is it ok to divide by 0.0?
I even wrote a whole book on Geometry for Programmers and it's half symbolic computations, half numeric so at least half of the book is concerned with floating-point numbers too.
But for this particular language, I wanted to avoid introducing another type so much, that I do all the arithmetics on decimal strings. Like in COBOL.
There is one unorthodox rule: the precision of the result is the maximum precision of the argument. The rule means that 1/3 and 1.00/3 result in different numbers. The former is 0, and the latter - 0.33. The rest is business as usual.
Except, of course,
$ claude mcp add the_namingless_programming_language \
-- python -m otcore.mcp_server <graph>