Show HN: I created a language called AntiLang – breaking all the conventions (siruscodes.github.io)

143 points by SirusCodes ↗ HN
AntiLang is an interpreted programming language written in Go. The basic idea of this is to keep all the logical parts of a language same, but reverse the structure of it.

The idea for this project came when I was having a long midnight conversation with my friend and thought of writing such a weird language. The initial draft was far worse than the current implementation; we thought of reversing the brackets and the language would be read from bottom to top. I'm happy that I dropped that idea

Technical details: As the interpreter is written in Golang, I compiled it to WASM, and the whole interpreter is running in the browser. For the editor, I'm using Monaco, the same library that powers the text editor in VSCode. I learnt how to build it while reading "Write an Interpreter in Go" by Thorsten Ball.

The project is opensourced - https://github.com/SirusCodes/AntiLang - do give it a star if you like the project.

131 comments

[ 2.6 ms ] story [ 196 ms ] thread
Thanks for reminding me how reading code feels when I just got started.
ugh I like it but I had to close the tab right away
I had fun, lost my mind, and a few friends.. probably. thanks!
I wonder if this can be considered an info hazard. Nice job, but don't do it again.
I will try my best to resist the temptation ;)
a very good language this seems to me like
A good effort and took me a minute to adjust to not knowing what's going on. Title made me hope for something more esotheric than just backwards code, though.
I'm sorry to disappoint you, maybe next time I will have something more esoteric
The control flow is basically smalltalk funnily enough, since it has effectively no language level control flow, if/while etc are all just messages in the form of

```

[condition] whileTrue: [code]

(condition) ifTrue: [code] ifFalse: [code]

```

A lot of people at Reddit told me that it resembles SmallTalk, Factor, and Forth. Being someone who started coding in last decade I have only heard the names but never saw the syntax, but true it looks surprisingly similar. I can't really fathom that people actually built stuff in those language (probably better things that we build today)
apl-family languages also often read easier right to left
Postfixes are easier to use, if you know how to use them. They're not only used in programming but also in spoken language:

    It is how, in this sentence, the verb works.
    
    It isn't how the verb works, in this sentence.
Sentences that simple are equally understandable using the verb as a postfix or infix. When sentences get longer that changes, though:

    It is how, in this sentence, which now has an extra clause, the verb works.
    
    It isn't how the verb works, in this sentence, which now has an extra clause.
In the first sentence, it's clear that the extra clause is something the sentence has, not the verb. The second example has what is called a dangling modifier, which is a clause at the end of the sentence that ambiguously could be describing more than one thing, in this case it could mean that either the sentence has an extra clause or the verb has an extra clause.

The lack of ambiguity also means it takes fewer commands to describe a single operation. For example, in an calculator with infix notation, which is what most people learn, calculating the multiple of four pairs of sums would require the following keypresses:

    ( 5 + 6 ) x ( 2 + 7 ) x ( 3 + 8 ) x ( 7 + 9 ) =
With an RPN calculator, which has a postfix notation, it would only require these keypresses:

    5 Enter 6 + 2 Enter 7 + 3 Enter 8 + 7 Enter 9 + x x x
That's 24 in the first example and 19 in the latter, a reduction of over 20%.

My grandma never really learned how to use a computer and was generally not very good at complex instructions. When she learned to use a calculator, RPN was just as common as postfix notation, and she was able to figure out how to use RPN, but had trouble figuring out postfix notation. Nowadays, schools usually only teach postfix notation, so most students aren't exposed to the easier method, and will have to do extra work, in the short term, to lean it, but it means calculations will require less work, once learned.

tl;dr: Postfix notation is the Dvorak keyboard to infix notation's Qwerty keyboard. The former is easier and faster, but everyone learns the latter. Putting in the extra work to learn the former will save you more time in the long run.

Thanks for the explanation. I don't agree with your point that postfix is easier; maybe it's because most people are not used to thinking in postfix. It can be faster and easier for computers, but we build software for people, and it increases cognitive load on the users.

It might be fewer keystrokes, but I feel for most people, the difference in keystrokes doesn't justify the cognitive load it adds.

I grew up using HP RPN calculators and it was actually easier in the long run. It isn't in the keystrokes either, but because when I was calculating an algebraic expression, I would have to setup a "query plan" in my head on how I would input all the values and then often I could simplify things as I entered them. I got rather good at manipulating algebraic expressions as a result, and it served me well in college.
(comment deleted)
This is what perl looks like to people who don't know it.
Shouldn’t the interactive mode be called `antilang lper`?
Good point, but repl is just fine in my opinion
you may want to take a peek at INTERCAL. you think GOTO is bad? wait until you have to deal with COME FROM, especially in "Threaded INTERCAL" where arriving at the location that multiple active COME FROM statements are pointing to simply jumps to all of them simultaneously.
Is COME FROM significantly different from an event-driven architecture?
Not in the sense of flow of execution. But you'd explicitly fire off an event, no? COME FROM is registering invisible triggers at arbitrary locations.
Might implement this feature.

Raised an issue on GitHub - https://github.com/SirusCodes/AntiLang/issues/7

Does the language have continuations? If you're going for fun control flow features those are both much more useful and mind bending. They also fit the theme you have going since most languages don't have them and they're effectively the reverse of a function call.
also you can set the probability of a command to be executed at all!
Honestly, it doesn't look that bad, I could get used to that syntax. It's not really objectively worse nor better than the normal syntax we have in programming languages now.

I think a true AntiLang would also break conventions of how programming itself works too. This just seems like a normal language with weird syntax.

This makes me wonder whether the usual order for function application is an artifact of the S-V-O sentence structure in western European languages. Maybe if Euler had been raised in a language with a S-O-V sentence structure, we would write (x)f instead.

Plus, reverse function application/composition works better with our tendency to write the domain of the function before its range. Given f : A -> B and g : B -> C, the composite function is written as g o f which is mildly annoying.

Even in modern mathematics, the postfix notation is used in some places. One example I can recall is permutations.

Consider the following permutation:

  f = (1 2 3 4)
      (1 4 2 3)
This is typically written in cycle-notation like this:

  f = (1)(243)
If we look at the cycle notation, f maps each symbol to the one on the right. For example, it maps 1 to 1, 2 to 4, 4 to 3, and 3 to 2.

Now if we want to find out what happens to the sequence (4 3 2 1) when we apply f to it, we normally write it as

  (3 4 2 1)f
The argument comes first. The function name comes next. It makes it quite convenient to work with permutations, composition of permutations, etc. For example, the result for the above application is:

  (3 4 2 1)f = (3 4 2 1)(1)(243) = (2 3 4 1)
You see it in some treatments of abstract algebra too. (Which are not unrelated to permutations in any case.) I think at least one book used in my undergrad used postfixes for function application, but I can't remember which.
Was it perhaps 'A course on Group Theory' by John S. Rose?
Definitely not. (He may do that, I just hadn't ever heard of that book before today.) I think it was one of Isaacs, Herstein, or Artin, but I'm a bit too lazy to dig through my old books at the moment.
> Definitely not. (He may do that, I just hadn't ever heard of that book before today.) I think it was one of Isaacs, Herstein, or Artin, but I'm a bit too lazy to dig through my old books at the moment.

Herstein does it.

> Maybe if Euler had been raised in a language with a S-O-V sentence structure, we would write (x)f instead.

This is an interesting question but it's further complicated by how frequently Euler was writing in Latin, which usually prefers (but doesn't require) SOV order.

I just looked up a random text by Euler (https://scholarlycommons.pacific.edu/euler-works/298/) and I see

> Genus autem secundum eiusmodi problemata complectetur, ad quae soluenda coordinatae ad duo curuae puncta pertinentes simul considerari debent; cuiusmodi erat problema de traiectoriis reciprocis, illudque problema catoptricum, quod ante aliquot annos tractavi. Cum enim in his continuo bina curuae puncta inter se conferantur, et coordinatae ad ea pertinentes in computum ingrediantur; per principium continuitatis effici debet, ut bina haec puncta ad eandem lineam curuam referantur, sicque aequatio inter coordinatas unicum punctum spectantes eliciatur.

I put all of the clauses' main verbs in italics. All of them except for "erat" ('was') are at the ends of their respective clauses! So, Euler had quite a lot of experience writing SOV sentences in technical contexts.

I just had another thought about this.

Programmers are pretty accustomed to thinking of functions as verbs. In some programming styles, we're even explicitly encouraged to name our functions after verbs describing what they do.

Sometimes mathematicians think of functions more as expressing a complete (and sort of timeless) relationship between a domain and a range. That relationship, considered in its totality, can be thought of as a noun rather than a verb.

Earlier mathematicians might also have tended to think of functional notation as referring to individual specific instances of the result of the calculation that the function refers to, like "the sine of 1/2" being a specific number (the result of computing the sine of one half, or, alternatively, the output of the function given a specific input). So when writing something like sin(1/2) they might not be thinking "please [person or machine] perform this computation on the number 1/2 right now" so much as "[I am here indicating] the specific [timeless and inherent] number that is the sine of 1/2". That's a different way that a function could be seen as a noun, essentially seeing the function as merely a way of referring to its output.

Calculus and real analysis start to give stronger reasons for thinking of functions as abstract objects in their own right (e.g. because we can say something like "d/dx sin(x) = cos(x)" or even "the derivative of sin is cos" without thinking about specific values. Or questions like "can a function exist that is continuous but not differentiable?". Maybe computer science then starts to give reasons for thinking of functions as descriptions of how to perform them (like source code or even machine code punched on punch cards) as opposed to references to their specific results (like a printed book of function tables, which someone else calculated ahead of time so you wouldn't have to).

This is all to say basically that maybe it's more natural for us as programmers to think of functions as verbs, but maybe Euler didn't conceive of them that way at all!

> This makes me wonder whether the usual order for function application is an artifact of the S-V-O sentence structure in western European languages. Maybe if Euler had been raised in a language with a S-O-V sentence structure, we would write (x)f instead.

Herstein's Topics in algebra uses that notation for function application. It's surely not a coincidence that he was Polish, and that that reminds one of RPN.

Maybe I've spent too much time with Forth, but the given example didn't look bad at all to me. Quite the contrary. The moment I reached line 2 (i.e., ",20 = count let"), I knew exactly what was happening. My mind immediately switched to postfix-mode. From there, everything made sense: logical, intuitive, even aesthetically pleasing!
A lot of people said this, why building this I had no idea of how these languages looked. Now that I know about them my project looks like a cheap imitation of them :(
Don't be disappointed. That's cool! You've independently discovered something.
>>> Maybe I've spent too much time with Forth, but the given example didn't look bad at all to me. Quite the contrary. [...]

>> A lot of people said this, why building this I had no idea of how these languages looked. Now that I know about them my project looks like a cheap imitation of them :(

> Don't be disappointed. That's cool! You've independently discovered something.

I completely agree with mkingston's comment. Whether or not this project resembles other programming languages is beside the point. What truly matters is the joy you had building it. As Alan Perlis wisely said, "I think that it's extraordinarily important that we in computer science keep fun in computing."

This is a fascinating project, and in that same spirit of playful exploration, I'd like to share a minimalist, esoteric postfix language for drawing on a canvas that I built some time back: <https://susam.net/fxyt.html>.

Breaking conventions has been a thing for about as long as conventions themselves. No need to be hard on yourself. Next thing you should look at is Malbolge, very convention-breaking ;)
It looks like cat ran over the keyboard ( + _ + )
The solution is not to stick strictly to prefix, postifx, or infix notation. Instead, all three should be valid.

All these should be equivalent:

  + a b + c d + 
  + + + a b c d
  a + b + c + d
  a b c d + + +
More insidiously, these should be equivalent:

  x = a + b
  = + x a b
  x a b = +
  + x = a b
  x = a b +
  x a + b =
Think you should rename some of the keywords to more naturally suit their positioning.

Like, rather than

    [condition] if 
        <code> 
    [condition] else if 
        <code> 
    else 
        <code>
if you have

    [condition] then
        <code> 
    otherwise [condition] then
        <code> 
    otherwise 
(Though 'otherwise [condition] then' breaks your pure postfix style).

Similarly "while" could be "yields".

And "let" could be "over" (CB radio-style):

    10 = age over;
Someone on X suggested to make it logically reverse, so true would be false and visa versa.

We can go ahead and make + behave as - and similarly for other operators.

Do you want to lie to your users, or do you just want to use unconventional operators? If the latter, you could consider using something like ruby's `unless`.

(the number of times I've noticed code that does `unless !some_condition`... which is of course just the same thing as `if some_condition`)

Makes sense; I need to think about which would be better.
I love it. I want a python-esque version that's whitespace sensitive. But only for trailing whitespace.
You're a demon living in hell, aren't you? No one on Earth could be this evil.
That is something I might do... Seems pretty horrible
Why need it be trailing at all? Just use Whitespace (esolang)
The idea of a trailing whitespace sensitive language is... amazing.

Get real torturous with it and make the amount of whitespace the line number, ala BASIC. 3 trailing spaces is line 3 for instance. Tab character counts as powers of ten separator? Space space space tab space space is line 32?

If you used leading whitespace you could wind up with something like old basics where you had to number your lines. It was great, you'd always skip 10 between lines so if you had a bug you could stuff a patch in between existing lines at *5.

For the whitespace, you'd have to know how deeply to indent the outermost part of your code.

So if you add an if block to a for loop, every line of the code has to be indented and only the contents of the new if would be at indent of 0.

I am not going to write this pseudocode in AntiLang because I am not that much of a masochist.

    for ( foo in somearray )
  doStuff(foo)
    end
becomes

      for ( foo in somearray )
    if ( condition )
  doStuff(foo)
    end
      end
If you antilanged this the rest of the way, you could have a common `start` to indicate the start of a block and then replace the `end with the actual conditional.

    start
  doStuff(foo)
    for( foo in somearray )

This gets horrible pretty quick. So as terrible as trailing line space is, leading line space is quite possibly worse.

Which leads me to the ultimate conlusion -

  White space should be balanced.  If you need 4 spaces indent, you also need 4 spaces on the end of the string.
More or less just writing each line backward (token by token).

Q1: How close could one get to a grammar that would basically recognize C with each line reversed character-by-character?

Q2: What would a language look like where instead of being oriented into right- or leftward-marching lines, it was oriented into downward-marching columns?

Honestly, you have done the most superficial thing here. It’s fine, but K is much more interesting in terms of encoding ideas differently than ALGOL style languages, just as one easy example.
Didn't knew that it existed, I will take a look at it!
Nice! Feature request : conditional expressions should start with the "else" branch.
That's a good idea but the current flow is from top to bottom but this would change that, someone would be reading it from top to bottom then at the end read if case then go back to top to read else case. I don't want to do that.
> Initially I thought to use , for float but ended use using . for floats.

Better - check the computer's region setting and use the local language convention, so decimal point is "." is English speaking regions, and "," is Euro regions, and who knows what else in other regions. That way code might work in one location but fail in another ;)

Good idea, but I suggest swapping . and , regions keeping with the spirit of the language.
Is that true? I thought for all languages only "." is defacto decimal point in all languages. Never knew it changes with locale.
The hegemony of software only accepting . has de facto pushed the standard everywhere for computers, but here in France I still write with a comma, but type with a dot.

A few years ago Excel and some other softwares started to be locale dependent and I never wanted to burn my computer this much

French dev currently working for a French but global client, here. The UI of the timesheet app is in English but the fields only accept `,` as decimal point. It's so needlessly confusing.
That's one of the great boons of localization. The webapp knows you're in France, so it tries to do the right thing, while giving you a US English UI. I experience the same thing, but got used to it somehow.

Another good example is how "İ" is popping up everywhere, even in English, because of misconfigured locale settings and how changing case is affected by it. We (Turks) are responsible for that, sorry (We have ı,i,I,İ =D ).

No. Some languages use . for thousand (or even hundred) separators.
Very much so.

Dot is often used as thousands separator too.

I remember the first time I saw 10,000 as a price and thought: 10 bucks? So cheap. But also: who needs 3 decimal points for a price?

Looks like its more or less 50% of the world [0].

[0]: https://en.wikipedia.org/wiki/Decimal_separator#Conventions_...

To add to the complexity of the whole situation, some countries don't separate by thousands (every three zeroes). India uses a 2,2,3 system (crore, lakh, thousand).

10 million = 1,00,00,000

https://en.wikipedia.org/wiki/Lakh

> who needs 3 decimal points for a price?

Petrol stations... I have no idea how widespread this practice is, but at least in Germany fuel prices have 3 decimal points to better confuse motorists. The third number is usually displayed smaller and is of course always a nine. So, if you see the price for a litre of diesel at e.g. 1.62⁹ €, you might forget to round it up mentally.

For example, German speaking countries use a comma instead of a decimal point, whereas the latter is used as a group separator. The German word for decimal place is "Kommastelle" (= "comma place").
It’s “,” in Poland, and a dot(or apostrophe) as thousands separator.

That’s why in region settings on your computer you will find not only date/tome formatting, but also the number format.

International standards say that either dot or comma is acceptable as decimal separator and thousand separators are optional spaces, typically a half space when properly typeset.

    ISO 31-0 (after Amendment 2) specifies that "the decimal sign is either the comma on the line or the point on the line". This follows resolution 10[1] of the 22nd CGPM, 2003.[2]

    For example, one divided by two (one half) may be written as 0.5 or 0,5.
This is so evil it has already been implemented by Microsoft (Excel sheets...)
No surprise to see that coming from microsoft.
yeah, to keep it in spirit also add localization of function/operator names and make sure only the locale version works not all at the same time.
This is on par with how Java WebStart locale reporting works between Windows and Linux.

AFAIR, Windows always reports US_EN for the locale, so you can write locale unaware code everywhere, but when running on Linux, you get the correct locale of the system (of course), and things break spectacularly.

I remember debugging an integer overflow, and I literally facepalmed following a "you didn't do THAT, did you!?".

The thing they did was parsing the date from the date string (formatted for system locale, without giving a specific locale) Java returned to them instead of fixing the locale and getting the date or getting the parts with relevant functions.

I have a relatively short fuse for people who doesn't read (or at least skim) the manual of the library they're using.

Everyone knows 'goto' is bad, so a language like this needs 'comefrom'
Interesting thought... But how will that work? The statement would be executed only it comes from that line?
It is reverse GOTO. After (or before - your choice) that line is executed, execution jumps to the comefrom.
The compiler inserts a goto to the statement following the comefrom statement into the line specified. :^)

If multiple comefroms to the same line are specified, the comefrom to jump to is selected randomly.

It is like a regular goto, but with the label and statement reversed.

So instead of

  label: 
  do_something
  goto label
You have

  comefrom label
  do_something
  label:
Really, that's just syntax. Declare your labels with "comefrom", and ":" means "jump". But it looks a lot more confusing this way.
It's just syntax until you decide to support both.
Ok, so:

  comefrom label
  goto label
  label:
A brand new way to lock up your machine.
INTERCAL already has this in its language