The Luon programming language
github.com
github.com
I’m intrigued by the LeanQt library as well that the IDE uses (https://github.com/rochus-keller/LeanQt) too.
Thanks. I e.g. re-implemented the Smalltalk-80 VM in Luon (see https://github.com/rochus-keller/Smalltalk/), and I consider implementing an Interlisp VM (see https://github.com/rochus-keller/gingko/) which uses LuaJIT, which was an important motivation to interrupt my Micron language project to implement Luon.
Actually an "edge over more currently popular languages" from my humble point of view is the goal and maintenance of simplicity. The term is subjective, but if you look at many of today's completely overloaded languages, it is intuitive to understand.
Simple solutions are also less prone to errors and easier to modify and expand. This reduces the probability of errors and makes it easier to maintain and update the system.
Simplicity makes systems (and programming languages) easier for users to understand and use. This leads to greater user-friendliness and reduces the learning curve, resulting in a more positive user experience and, in turn, a lower error rate.
I'm sure there are many more reasons (apart from the obvious proof by authority, which is based on the statements of, for example, Einstein or Wirth).
Wow!
And by the way the startup speed of the IFE is just insane! it is actually faster than my simple text editor!
Do you see there being a way to make a TurboLuon with current specs?
I get the appeal to write an IDE from scratch, especially if you are already an expert in writing GUIs with your framework of choice! I wonder if it would make more sense to spend that time writing a language server protocol daemon. That way, you could make your language available in any IDEs your users like that support LSP.
I imagine you’ve been playing music most of your life. How long did it take you to bring all of this together and start “one man band” improvising?
There's a lot I like about Lua but it so happens that a few days ago I spent longer than I'd like to admit debugging a trivial typo for Advent of Code day 5 that would have been caught by this.
Wondering if Luon will also prohibit or at least warn about storing nil in a table.
Replacing the 'everything is a table' with records, arrays and hashmaps is also a thoughtful improvement IMO.
Just confirming one point to make sure I understand the licensing implications correctly. Since the compiler transpiles to LuaJIT, and since that's just data, using output of the (GPL v2/v3 licensed) Luon compiler (i.e. the LuaJIT output) in a commercial/closed source project without divulging the source of said project should be fully kosher right?
Am guessing the answer is most likely in the affirmative but just making sure.
But I am glad that it went with Oberon's 0-based array indices, as opposed to Lua's 1-based table indices.
https://github.com/rochus-keller/Luon/blob/master/specificat...
JS:
for (let i = 0; i < items.length; i++) {
groups[i % 3].push(items[i]);
}
Lua: for i = 1, #items do
table.insert(groups[((i - 1) % 3) + 1], items[i])
end
Don't get me wrong. I like Lua, I've made my own IDE for it, https://plugins.jetbrains.com/plugin/14698-luanalysis, but this is definitely not an argument in favour of 1-based indices.groups[1], groups[2], groups[3] = items[i], items[i+1], items[i+2]
If the group count is dynamic, you can just loop over groups instead, and then step through items by #groups, inserting.
It's also worth noting your solution exhibits similar off-by-one behaviour. The left hand side constants (integer values) do not match the right. It's error prone.
>You can't add 1-based indices together like you can zero-based indices.
I think you are right but I am unable to articulate why. But I think 0 based indexes are able to fruitfully capture "going nowhere" iteratively than 1 based indexes, which do require a decrement in that circumstance.for each group:
for i in steps of #groups:
assign item to this group
I think that’s a lot easier to comprehend than the modulus trick
groups=new Array(3).fill([])
items.reduce(function(a,x,y){y=a.shift();y.push(x);a.push(y);return a},groups)
Array languages typically have a reshaping operator so that you can just do something like: groups:3 0N#items
Does that seem so strange? 0N is just null. numpy has ...reshape([3,-1]) which wouldn't be so bad in a hypothetical numjs or numlu; I think null is better, so surely this would be nice: groups = table.reshape(items,{3,nil}) -- numlu?
groups = items.reshape([3,null]) // numjs?
Such a function could hide an ugly iteration if it were performant to do so. No reason for the programmer to see it every day. Working at rank is better.On the other hand, Erlang is also 1-based, and there's no numerl I know of, so I might write:
f(N,Items) -> f_(erlang:make_tuple(N,[]),Items,0,N).
f_(Groups,[],_,_N) -> Groups;
f_(G,Items,0,N) -> f_(G,Items,N,N);
f_(G,[X|XS],I,N) -> f_(setelement(I,G,X),XS,I-1,N).
I don't think that's too bad either, and it seems straightforward to translate to lua. Working backwards maybe makes the 1-based indexing a little more natural. n = 0
for i = 1,#items do
if n < 1 then n = #groups end
table.insert(groups[n],items[i])
n = n - 1
end
Does that seem right? I don't program in lua very much these days, but the ugly thing to me is the for-loop and how much typing it is (a complaint I also have about Erlang), not the one-based nature of the index I have in exactly one place in the program.The cool thing about one-based indexes is that 0 meaningfully represents the position before the first element or not-an-element. If you use zero-based indexes, you're forced to either use -1 which precludes its use for referring to the end of the list, or null which isn't great for complicated reasons. There are other mathematical reasons for preferring 1-based indexes, but I don't think they're as cool as that.
It’s such a binary, polarizing issue too, because .. here we are again as always, discussing reasons to love/hate Lua for its [0-,non-0] based capabilities/braindeadednesses..
In any case, I for one will be kicking some Luon tires soon, as this looks to be a delightful way to write code .. and if I can get something like TurboLua going on in TurboLuon, I’ll also be quite chuffed ..
To iterate over an array with "len" elements, it’s most elegant if “len” appears as a loop bound, rather than "len+1" or "len-1". Thus, in 0-based languages we use half-open ranges, whereas in 1-based languages we use closed ranges:
// real C
for (int i = 0; i < len; ++i)
process(array[i]);
// C-like language with 1-based indexing
for (int i = 1; i <= len; ++i)
process(array[i]);
But the second is inelegant when len is zero, because 0 isn’t a valid index at all, so it’s weird for it to appear as a bound.Naturally it's true that for collections and naïve indexing, 1-based is more natural. But those are rare places for bugs to occur, while interval calculations are a frequent place for them to occur.
Clearly I'm far from allergic to the other standard, but I come down on the side of the zero basis for that reason.
It’s also very natural to think of arr[i] as “i steps past the beginning of arr”. With one-based indexing arr[i] has no natural interpretation that I know of. It’s “i-1 (for some reason) steps past the beginning of arr”. The only reason I can think of to prefer that extra -1 in your formula is just because human languages (at least the ones I know of) work this way — the 42nd element of a sequence, in normal colloquial English, means the one 41 steps past the beginning. But I’m not sure if there is any logical justification for that.
I also, despite being American, find the convention used in many countries of numbering building floors starting with zero to be more logical. I’m on the third floor, how many stories up did I travel to get here? Three.
Also LED floor numbers in lifts (elevators) in India start from 0 for the ground floor, as do the buttons that you press to go to specific floors.
Also, Ground Zero.
Because then there is no good way to refer to the index before that point: You are stuck using -1 (which means you can't use it to refer to the end of the array), or null (which isn't great either).
> every programming language I know of that supports the concept of unsigned integer
Surely you know Python which uses a signed integer as an index into their arrays: list[-1] is the last element of a list. If they only used one-based indexing then list[1] would be the first and that would be nicely symmetrical. It would also mean that list[i-1] would NEVER refer to a value after ‹i› eliminating a whole class of bugs.
> It’s also very natural to think of arr[i] as “i steps past the beginning of arr.”
I think it's more natural to think of arr[i] as “the ‹i›th element of arr” because it doesn't require explaining what a step is or what the beginning is.
The exact value of ‹i› matters very little until you try to manipulate it: Starting array indexes at one and using signed indexes instead of unsigned means less manipulation overall.
> find the convention used in many countries of numbering building floors starting with zero to be more logical
In Europe, we typically mark the ground-floor as floor-zero, but there are often floors below it just as there are often floors above it, so the floors might be numbered "from" -2 for example in a building with two below-ground floors. None of this has anything to do with arrays, it's just using things like "LG" or "B" for "lower ground" or "basement" don't translate very well to the many different languages used in Europe.
The software in the elevator absolutely doesn't "start" its array of sense-switches in the middle (at zero).
But I guess they wanted to iterate from the end back [-1] to the start [0], making it easy to implement a rotating buffer.
This is what was once added to C#: arr[^idx], when this ^idx is mapped to a special object, typically optimized then out. arr[^0] means the last element.
A similar approach also works for slicing the types with range operator e.g. span[start..end].
Yes, but if you will eventually need to do steps on your array, you better opt for the framework that handles them better. I agree, that if your only task is to name them, then 1 based indexing makes more sense: you do that since diapers, and you do that with less errors.
_Western_ Europe. Eastern Europe prefers 1-based numbering. The reason, typically assumed, is that thermal isolation, required due to colder winters, causes at least one stair segment between entrance and the sequentially first floor.
Alternatively the ground floor is the first floor because it’s the first floor you arrived at when you entered the building.
The same point of view applies to 1-based indexing.
That said I prefer 0-based in programming and 1-based in buildings.
They never heard of making a UI, and just slapped buttons.
I suspect that floor numbering predates lifts (elevators) by centuries.
Stairs are ancient.
I mean, zero itself is a non-obvious concept. Its invention is a matter of historical record:
https://www.open.ac.uk/blogs/MathEd/index.php/2022/08/25/the...
... and we still use counting systems which predate the invention of zero, such as Roman numerals.
Mathematics as a discipline predates the invention of the digit zero. The concept sure, but the notation and building positional representations around it is around 2000 years old.
Ukrainian here. Multi-floor buildings always have at least one stair section to first floor due to need of thermal basement isolation. (I guess this is not pertaining to Western Europe due to more clement winters.) And, yep, it is called "first" floor. Using zero number is rare but possible (in this case it is called "tsokolny" floor) if a real "basement floor" is present, but in this case still 1-based numbering is preferred.
To be pedantic, "first" is associated with 1. And a circle does not have a "first" entry, whatever you mean by entry. I think what you're trying to say is that a circle is a continuous arc going from 0 to 360 degrees, but you should recognize that the "starting point" is arbitrary, any point will do, so there isn't really a "first", and that this is not the same as counting because counting is done with natural numbers, which are non-continuous. The problem of 0 VS 1 makes sense only in counting exactly because it's subjective whether you prefer to count from 0 or from 1. Because zero is the absence of anything, I find it hard to start counting from 0 (when you do, your "first" item is actually your zeroth item, and the next item would be the "first"??!), to be honest, despite being completely familiar with doing so since I've used 0-index programming languages my whole life.
I don't think this is true. They exist in other disciplines (maths for instance) that have no relationship with C or other programming languages from the 1970s.
> for 0 to count/len/num - 1
I will counter saying that such a for...to syntax is a relic of BASIC.
> or even better range syntax that is start inclusive BUT end exclusive
I know that your "better" is sarcastic, but I actually find left-inclusive+right-exclusive ranges fantastic. They allow perfect partitioning, easy calculation of lenght, etc.
> Arrays should start and end at whatever start index is required
I agree. An accommodating language would let you define both lower and upper bounds of an array, instead of its size.
OPTION BASE 1
or something like that, to change the starting index to 1.
APLCast podcast has an episode mentioning it where they all seem to agree that this is the worst of all worlds, makes sharing code and integrating codebases needlessly bug-prone, and the language picking a single indexing and sticking to it would have been better, even if the choice hadn't gone the way they would have personally chosen.
Yes, that seems bug prone, somewhat like having to have a config file per module.
Doesn't it predate that by a good amount? I would think it is a relic of the EEs who built the digital world, those early languages show a great deal more relation to the bare metal than modern languages. Creating an array whose index starts at 1 just doesn't make sense from the discrete logic point of view, you are either wasting an element or adding in an extra step.
But in this day and age how can a language not have ⎕IO ← 0?
If our species had established counting from 0 as the norm right away (element #n is the one that has n elements before it; you think of the number as the number of steps you have to move away from the starting point), then I suspect the reverse would not be true: I don't think anyone would find a situation in which counting from 1 is so much more convenient that it's worth going against the grain of established norm.
So in summary, I think we only think of counting from 1 as natural because it's in our culture. And it's in our culture because ancient superstitious humans had an irrational problem with the number 0.
> Arrays should start and end at whatever start index is required
That's what you were indeed able to do with Pascal and also Modula-2, but with Oberon, Wirth came to the conclusion, that other index ranges than 0..n-1 were not needed. In his 1988 paper "From Modula to Oberon" he considers it "inessential" and providing "hardly any additional expressive power", but causing "a hidden computational effort that is incommensurate with the supposed gain in convenience". I think, in the end, it is in the eye of the beholder.
Luon can indeed look similar to Oberon if you use upper-case keywords and semicolons, but there is not need for this. Both - Lua and Luon - have much in common with Modula-2 (given lower-case keywords). There are many elements in Luon which are pretty similar to Lua, e.g. constructors, pcall, most control and loop statements. But there are also significant differences of course, because Luon is a statically typed language and Lua isn't.
The point being that both the start at 0 and start at 1 camps can have it their own way.
Can confirm that about Pascal, since I had used it a lot earlier.
Don't know about the other Wirth languages.
>The point being that both the start at 0 and start at 1 camps can have it their own way.
Yes, but that is not the only point. Another reason, and maybe the more important one, is that having such custom array index ranges, can more naturally fit the problem domain. In fact you can even use user defined types for the ranges, e.g. so you can define an array with Sunday to Saturday as the indices and the values of (the equivalent of) an enum representing 1) weekdays and 2) weekends, as the corresponding values.
Then your code involving days and weekdays and weekends, will read more naturally, so will be easier to both read and maintain. And you only have to do those custom definitions once, up front, so it is not much extra work for the benefit gained.
In Oberon, Wirth kicked out everything to the bare minimum, including subrange types. Array indices in Oberon start with 0.
You could argue that Wirth overdid it a bit with the simplicity vs. Comfort features but that's probably also dependent on your preferences and the problem you want to solve.
There are a few languages where you regret that they did not win in favour of C++ and Oberon is one of them (and Modula 3). Not that C++ does not have its strength , but for many problems Oberon would probably have been the simpler fit.
Yet, when you compile to Lua, the GC and system programming features are moot.
It's rather curt. How about: Luberon