> We usually start to count from one. When a baby completes its first roundtrip around the Sun, they’re one year old, not zero years old.
And before they complete the first trip, they're 0 years old, showing that you start from a count of 0. The one is the result from the first increment.
If I ask you to count the number of red balls in a bag with only 3 yellow balls, then the initial count in your head is 0, you inspect the balls one by one, never encountering a red ball, and thus never incrementing the count. And then you pronounce your final count of 0. So that's counting starting from 0.
What you call "starting at 1" is not so much the start as it is the first increment, which need not arise.
For arrays, the first element is the difference between a size 0 array and a size 1 array. So the choice between 0-indexed and 1-indexed is like the difference between pre-increment and post-increment. Do we associate the element with the array size up to the element, or to the array size up to and including the element? The choice seems arbitrary
(personally I prefer using the same range of non-negative numbers for both, but language has a bias for associating "first" with index 1).
Nobody says that a baby is 0 years old. They use other words instead of numbers, or if they do use a number, it is X hours / days / weeks / months old.
0 in normal usage is always a negation of something existing. You don't start counting things that exist at 0 (as you pointed out in your example of number of red balls in a bag that have none). However, position zero at an array in zero-indexed languages is the first position within the array.
There's no getting around the fact that higher level languages inherited 0 by counting byte offsets, rather than positions, even if those languages don't required that every index within an array have the same size in memory (or occupy a contiguous block of memory), and that this inherited usage is at odds with the actual plain-language and true meaning.
Totally irrelevant to the the actual conversation but I have in fact put my infant child’s age as 0 because I didn’t think to count in smaller increments than years. So there is at least one person who says babies are 0 years old.
You start your argument with a missconception, 0 does not relates to the existance of something but rather the lack of. When a bank acount reaches 0 it does not mean money does not exist, neither it means the account does not exist, it just means there are no units there of that you are counting...
Of course they don't. They use decimals and say the baby is one week old or so.
As for indexing from zero, it's best understood as indexing the position just before elements in an array. Where things will be inserted if you insert an element etc...
While I agree about the points you made, you cant take the zeroth element out of your shopping bag, you take the first one. The array indexing operator gives you access to the nth element of your data store. IMO OPs point is valid.
You created an unfair definition of the array indexing operator. I can just as easily say that the array indexing operator gives you access to the element at index I, which starts counting at 0. That's not an argument.
The sequence of the cardinal numbers, i.e. of the equivalence classes of sets having the same number of elements, is 0, 1, 2, 3 and so on and those words in any language have been used originally for cardinal numbers, not for ordinal numbers.
For the sequence of the ordinal numbers, whose purpose is to identify the position of the elements of an arbitrary sequence, any sequence of arbitrary symbols may be chosen and fixed by convention.
Most languages had at least in the beginning special words for the first and for the second elements of a sequence, without any relationship with the cardinal numbers. Even in English that remains true, even if "second" is a more recent substitution of the older word used previously. Many languages have special words for the last element and for the element before it. Some languages had special words for the third element and for the third element going backwards from the last. So in some languages it was possible to identify the elements of a 6-element sequence without using words derived from the cardinal numbers.
However inventing a very long sequence of words to be used as ordinal numbers, in order to identify positions in sequences with more than 2 to 6 elements, would have been difficult, so in most languages someone noticed that there already is a sequence of words that everybody had to memorize when learning how to count and which had rules for being extended to any length. So the ordinal numbers were derived by using a suffix or some other derivation rule from the cardinal numbers.
There is no logical reason for using 1 for the first ordinal position, this is just a historical accident.
The reason is that the children have always been taught to count by saying 1, 2, 3 and so on, instead of being taught to recite the sequence of the cardinal numbers from zero.
All the languages have always had a word for zero, but those words were normally created by applying a negation to words meaning "something", "one" or the like.
Because of this, the words for zero were not perceived as having an independent meaning and there was no need to learn them separately when the recitation of the cardinal numbers was learnt.
Nowadays we have a much better understanding of the meaning of the cardinal numbers and we are aware that 0 is a cardinal number like any other, so the children should really be taught to count 0, 1, 2, 3 ... and not 1, 2, 3, ... like 5000 years ago.
In the natural languages there is a huge inertia. Even if one would decide that since tomorrow the ordinal numbers should be 0th, 1th, 2th, 3th, 4th and so on, everybody would still have to know that whenever reading older writings the sequence of the ordinal numbers was 1st, 2nd, 3rd, 4th and so on, so a change of the convention to a more logical one would bring no simplification.
On the other hand, in the programming languages you can ignore the legacy conventions and choose the best conventions. Using for ordinal numbers the sequence 0, 1, 2, 3 ... is the best choice for many reasons, which have been explained in the literature many times, e.g. by Dijkstra.
Choosing to start the ordinal numbers from 1 in a programming language just demonstrates a lack of understanding of what the cardinal numbers and the ordinal numbers are and a lack of practical experience in programming and of understanding of how the programming language will be translated into machine language.
The first programming language for which I have studied the machine code generated by its compiler, when I was young, happened to be Fortran, which uses indices starting from 1. Until today I remember how I considered ugly and error prone all the tricks that the compiler was forced to use in order to avoid in many cases to make extra computations due to the poor choice of the origin of the indices.
The point is valid, but the rationale is not, let me explain:
Caring about 0 based or 1 based indexing is, to me, a sign of someone who struggles with programming in general, or is stuck doing a lot of finicky conversion between the two.
Most modern, higher level languages have generally abandoned using indexing, instead (even c++) they have something like:
"for x in y do z"
1-based indexing is a bit more readable, but in the end, you have hardly any benefit. There are better paradigms, algorithms, which don't require indexing at all, which IMO is the majority of what programmers are doing anyways. Even if you need to process two same-size collections at once (the majority of the remaining legitimate uses for index based looping), you are likely working pre-sorted data and should consider using a zip or pair which eliminates the need for managing indexes.
You say "but aren't you just imposing your own style on others"? Not really, if we want clean, minimal code, there should be as few references to the underlying architecture as possible, even the fact that we are dealing with a list is an implementation detail (is it actually a list, a linked list, a stream, a dictionary, an event, etc...), so in the context of implementing a higher-level language, this point is not only irrelevant, but shortsighted. If you are creating and looping over lists, you are likely not doing anything interesting, which is the whole point of programming in higher level languages, to do interesting things simply, right?
As for the remaining use case when we do actually want array based access, typically you find this in high-performance, architecture aware applications - then we actually want random memory access. We may even want to deal with explicit memory offset, which is what 0-based arrays are good at doing (often times the array is syntax sugar and we are literally assigning to/from pointer offsets).
To bring this around to my previous statement: "Caring about 0 based or 1 based indexing is, to me, a sign of someone who struggles with programming in general, or is stuck doing a lot of finicky conversion between the two."
The reason this comes up in the first place is that there is a divide. 0-based arrays are arguably much better for low level activities, and higher level languages generally left them in as a familiarity. Translating from 1 to 0 based arrays is not any easier than translating from 0 based to 1 based arrays, 1-based arrays provide just as much confusion and less familiarity in these cases. This is not a good thing.
Now I suppose Lua is trying to be something weird, a "high level" low-level language. Maybe that's fine, but it is weird, and people are right to be put-off by the change. If you are looping over bananas in lua, maybe you're not really using it the way it was intended, if you are doing memory-level access then its unnecessary language cruft.
I wonder if it has something to do with a cultural perception of how life and the world in general is perceived. As in, "He's currently experiencing his first year of life" vs. "He has lived for one year", kind of "the journey" vs. "the destination". Seeing the present more than looking back the past. In the same line of thinking as "What you're doing" vs. "what you've done", or maybe even "making progress towards goals" rather than "accomplishments".
Seems like a healthier and maybe even more productive way to see the world. I don't know enough about Korea to say whether that has anything at all to do with the way they count ages, but I feel like it's an interesting thought regardless.
It's amazing to me that the top comment on a post that has very little to do with indexing -- basically waves at the topic in passing -- is about indexing, AND that when it's pointed out that this is not a very scintillating topic of conversation in this day and age, all of the responses to that comment are just more pedantry about indexing. How does this trivial topic hold such fascination for people?
>If I ask you to count the number of red balls in a bag with only 3 yellow balls, then the initial count in your head is 0,
Sorry, no. Humans count from 1. That's just a basic fact reflected in the history of numbers, which at early stages often didn't treat 0 as a number, but as a special case. And if you would say "I counted zero red balls" most people around will find it an unusual wording. Normal way of saying it doesn't involve mentioning 0 at all: "It's empty", "There are no red balls" etc.
That special casing in English of no/none/empty is as much an artifact of lost germanic grammar cases in English as it is anything "natural" or inherent to how English speakers count.
I have no problems with people advocating from 1-based indexing, and I often disagree with EWD, but advocating for 1-based indexing with out referencing EWD's reasonably well known take[1] is going to damage the ethos of your argument.
"In corporate religions as in others, the heretic must be cast out not because of the probability that he is wrong but because of the possibility that he is right."
0-based indexing is not universal in math either. We often index time from 0, but when we refer to components of a vector, we often index those from 1. For example, I'd write or see things like $\sum_{k=1}^N f(x_k)$ where $x$ is a vector. In these cases, 0-based indexing would look out of place.
> If I ask you to count the number of red balls in a bag with only 3 yellow balls, then the initial count in your head is 0, you inspect the balls one by one, never encountering a red ball, and thus never incrementing the count. And then you pronounce your final count of 0. So that's counting starting from 0.
Arrays aren't just used for counting though. For example, the origin in cartesian coordinates is "0,0" and not "1,1". Not to say that I disagree, I don't think I've come across a situation where 0-based indexing would have been better for me personally. But there are definitely a lot of situations where I can imagine it would be preferable, like pretty much whenever you're doing math with them.
It is a bit of a curse that every time this article ends up anywhere, the discussion quickly devolves onto the merits of 0-based indexing. I have a very strong opinion on this which is that "it doesn't matter". You use whatever paradigm is real in the language you're working. The context switch is not larger than the context switch between languages anyway. Lua is not the single 1-based indexing language out there, it used to be much more prevalent. I bet if JS had used 1-based indexing, half the people on this comment threads would just be praising the benefits of 1-based indexing. Sorry for the rant, but the reason I wrote that article in the first place is because I of such threads.
One thing I agree with many people here is that my baby example is not the best example. It is not wrong, but it is phrased in a confusing way. I'm tempted to edit the article and fix it or replace the example with something better. After all, the example is not important, it was just an example.
One aspect that many people don't realise, one that I couldn't really dive into in the article, is how simple and small the Lua source-code actually is. IIRC it is about 70 files of plain old C that can be compiled by any C99 compiler. That is very refreshing. The amount of power and flexibility you have with Lua when paired with the simplicity and maintainability of the Lua source-code itself is something we should all cherish and praise. Lua is small and nimble, it is small enough that you can vendor all of it in your project if you so want.
Anyway, I like 1-based indexing, they make my loops prettier. And yes, it was just a joke, I'm using iterators like everyone else and indexing hardly matter.
I think the reason is that the indexing paragraph comes across as quite misinformed. The age of the baby would be 1 year even in 0 based indexing (age is a duration, not an index). And pointers are only one of many places where 0 based indexing is more natural.
I use Lua a fair bit in my day job. The comment about it being a minimalist toolkit that allows you add scripting on top of domain specific tools is spot on. That is exactly the use case that Lua shines in.
My favorite feature is the way that tables and closures allow you build up just as much of OOP as you need for the application. It feels very empowering. I'm working mostly solo, so I've been able to establish my own conventions and stick to them. Doing this as a large team would be fraught with peril and require strict standard practices to maintain an intelligible code base.
I was surprised the author didn't mention my #1 complaint, variables default to global scope AND initialized. If you misspell a variable name Lua will happily initialize it to nil and run with it. This almost always results in subtle incorrect behavior instead of an error.
Amusingly, in a sense, your complaint about global variables is because of what you like about how generally simple Lua is plus how powerful its tables can be.
i.e. The global scope is a table, and variables are keys on that table, so of course the value of any variable is `nil` because that's how tables work and Lua isn't going to special case a table just because it's a magic part of the environment.
...but, naturally, since the global environment is a table, you can set a metatable on it and require that variables be declared before you use them. :D
To be clear, I'd like all tables to throw an error on unresolved key names. And I can't think of any cases where the current behavior is necessary for any of the features I like.
I think the design decision to have unresolved names result in defining a new variable, initialized to nil, is the result of a different design goal. I'm not sure what that goal was, something about fault tolerance maybe. In practice I find this feature means I need very thorough test coverage, or a metatable, as you've noted, or a static analyzer, as others have noted. Or a combination of all the above.
Also, do not forget an easy and powerful sandboxing that you can achieve with Lua by selectively including/excluding parts of its std library at compile time and running Lua itself in a thread with no (or minimal) shared state. This alone is priceless when embedding scripting into an application.
Regarding globals, defaulting to local scope is a thorny design choice. Try this in python:
def make_counter():
def incr(x):
total = total + x
return total
total = 0
return incr
I think Lua would be a better language if they were to eliminate global variables entirely, and flag as a load-time error any undeclared variable that is not present in the environment supplied to `load` or `loadfile`.
Setting __newindex on the global table to throw errors is a not-too-bad solution, but these will be run-time errors, so to catch them all you need good code coverage (yes, you want that anway) and uncouth third-party libraries can trigger those errors.
EDIT: re-reading your comment I think I see now you were agreeing with me. :-) Leaving this here as elucidation.
That python code throws UnboundLocalError, which seems like what it should do to me. If you move total = 0 to before def incr(x): then incr closes over the local total variable. Every call to make_counter returns a new counter with its own state.
The equivalent Lua defines total as a global (whether total = 0 is above or below). Multiple instances of incr are all changing the same global variable. That doesn't like the intended behavior.
Python would be a better language if it had Lua's local. Because Python would have true lexically scoped variables that way, instead of the current system.
> If you misspell a variable name Lua will happily initialize it to nil and run with it. This almost always results in subtle incorrect behavior instead of an error.
If your code editor doesn't warn you that you're potentially reading a variable before writing to it, then you need a new code editor.
Originally it was a configuration language. That global syntax makes it friendly to people writing configuration files. Anyway, if that bothers you, you might want to use luacheck or some other linter which will pick it up.
> my #1 complaint, variables default to global scope AND initialized.
Lua, originally, was a configuration language. You know, the sort of use-case you use a .ini (or these days, YAML) file for. Except you have realized you need some logic to set your flags, so you decide to make your config file programmable.
It has evolved to its current form but these roots remain. They should have added a 'strict' option.
I highly recommend if you're interested in Lua checking out a few of my favorite Lua projects...
1. Moonscript (https://moonscript.org/) the compile-to-lua language powering Itch.io, it's amazingly pleasant to work with and I was amazed porting a JavaScript tool to Moonscript that the moon version was more readable despite my lack of familiarity with the language.
2. Redbean (https://redbean.dev/) the single-file, cross-platform binary magic web server that's been all the rage lately. Super fun to embed Lua apps into a single binary and build small web apps.
3. Pico-8 (https://www.lexaloffle.com/pico-8.php) the famed "fantasy console" a fake old computer with fake old requirements that forces you to get creative as you write games and apps with the limited resources, of course, all apps for Pico-8 are written in Lua.
I think the moral here (especially with the last two projects listed) if you like when computers get weird you'll like Lua. I also recently compiled the Lua runtime into wasm to make some of my scripts portable to the browser, it was simple and easy, including modifying the language code just a bit.
Lua is a little wacky, but there's something so fun it brings me back to the early days of writing code when everything was simple and just a little off.
Odd to suggest that if they're interested in Lua, that they should check out Moonscript which is a different language altogether (although it compiles to Lua). But if you insist, something a little more Lua-ish is Teal[1] (gradual types ala TypeScript) or Pallene[2] (companion typed subset of Lua meant to generate optimized C libraries for use with Lua).
There's also my IDE which just bolts on types (both structural and nominal) to regular Lua - no transpiling as the types are defined with comments (or inferred).
Also TIC-80, a fully open "fantasy console" that uses Lua by default, but also supports other languages like JavaScript and Fennel (a Lisp-like variant of Lua).
To clarify, Fennel compiles to Lua, so you can use it in most places that use Lua. TIC-80 appears to embed Fennel, so you don't have to bring the Fennel dependency.
I would add Defold to that list, it's a pretty nice little engine if you're making casual games. I used it for my team's project for this year's TOJam.
One Lua thing that is wacky is that you cannot have large integers as keys to a table. Lua converts {2938433:"hello", 983748323:"hi"} to positions in the array portion of a table and creates a giant 99.999% empty table eating all the RAM.
Unless somebody knows something I don't on how to make it work.
i don't think that's true - lua stores sparse arrays efficiently. the table above should only have 2 slots allocated. see [1] or [4] for details on how lua implements sparse arrays.
He phrased it wrong. What he probably meant was: The first year in the baby's life is – well – the first year, not the zeroth.
It doesn't matter much, because we will never reconcile the "I'm counting objects" and the "I'm pointing at addresses" (or the "I'm doing math… something something Dijkstra") people.
there's definitely something deeper and more significant between start at zero and start at one.
something which mathematics alone is not sufficient to uncover, it requires more foundations/logic to be relevant; it requires computer science (but focused as 'computology' and pure theory).
in mathematics this becomes more aparent with ring algebraic-structures; and when considering exponentials/logarithms/roots and powers.
a way to bring into focus what I'm trying to point out, is to consider the historical development of counting numbers. In a sense, it is not until the foundations are thoroughly considered (which is more logic than mathematics) that the importance of starting from zero really shines. In this 'historical sense' we started at one, and then went back and realized that it's more 'technically correct' to start at 0; this same foundational redefinition makes it important to note that zero is the only natural without a predecessor.
an alternative way to point at this 'deep and significant' issue, is to consider how zero is the additive identity and one is the multiplicative identity; algebraic-rings having both, and the distributive property being the axiom which links both. (I tend to focus on how both elements are identities; they're both instances of an 'identity' element; two sorts of identity).
Having heard _insane_ crap like "Noone uses Lua in production" at a Fortune company (while the genius who uttered that sentence had a device in his pocket that was _definitely_ running Lua), I agree that it's a misunderstood language.
However, if anyone went as far as learning about things like arrays indexed at 1, they are far more aware than the average software engineer. They actually spent time looking at it.
Lua is a victim of its own success. It's small(the runtime is tiny and can be made even smaller by shedding features), it's mostly uncontroversial, it does the job, it's fast. It can be easily sandboxed so you don't hear much about vulnerabilities. It's just there, invisible, in almost every modern computing device.
The games industry is an exception. They have readily embraced it, for very good reasons. Browsers should have used it from the get go, but that ship has sailed.
Next time you think about having scripting abilities in your application, consider Lua. Same way someone should consider Sqlite if they are contemplating creating a new file format for their application. It's a pretty good tool.
Lua's problem is that JavaScript exists. They are fairly similar, but there are a lot more JS devs. QuickJS is about the same speed and size, so a lot of teams seem to be gravitating in that direction.
Also came here to comment about 1-based arrays :-)
It's not about which convention is "right" and which is "wrong" — they are all conventions, after all. It's about context switching, and the additional cognitive load that a programmer coming from a language where arrays start at 0 will have. An entirely unnecessary chore.
And, in particular, Lua is expressly designed to be embedded in C. By design, in order for it to be useful, you're expected to define your own functions in native code and make them accessible through Lua.
Given that, no matter what, you're going to be stuck context switching, since C is zero-based. I wish Lua had picked 0-based indexing not because it's better, but because it's consistent with the host language Lua is designed to be embedded in.
Fun fact about this: Lua is embedded in MediaWiki (the software that runs Wikipedia and also a lot of other wikis). And in MediaWiki template variables are also 1-indexed (no comment on this particular design choice). So the 1-based indexing of Lua becomes an utterly fantastic feature not just because it's convenient but also because a lot of people writing code are super new to programming, and any barrier to entry (like having to shift your indices when you go from the template to the module you're invoking) would potentially stop someone from writing any code at all in the first place.
So for this one use case at least, the 1-based indexing is pretty nice.
Wasn't Lua created by a professor? People in academia tend to develop projects aimed at people with no real world experience, and have often these weird design choices that do not map well in the real world with real code.
OT: which is why I think really great stuff like Racket (maybe even Haskell) have a hard time breaking through. The intended audience is people that have never seen a programming language, not tired engineers with deadlines.
Sometimes uniformly using 0-based indexing is simply moving a cognitive burden to later time or to a different person. For example some bright mind in my industry has decided that all numbering of quite real and physical entities (not addresses or offsets), like chassis, devices, blades, ports, sessions etc. should be 0-based. This let to a huge number of issues writing code, writing conversions, debugging, and most of all - testing this. Every time you need something wired to port 1, now you need to also clarify that that's not first port, and so on. But we got very hype and moderns numbering system instead. Amazing innovations :) .
Offsets are important in a programming language where you are regularly dealing with memory addresses, not nearly so much in one where memory access is almost completely abstracted away.
It is ludicrously unlikely that aliens would be comprehensible at all. Several of Lem's SF novels try to get this idea across, Solaris is most famous, though I'm more of a Fiasco person myself. An octopus is very closely related to you, far more so than any conceivable space aliens - and yet we have no idea what the fuck is going on in there. When you look at an octopus in an aquarium is the octopus wondering whether you are self aware?
We have assumed that aliens would do arithmetic, because we do arithmetic, and it seems very fundamental to us, but that might just be a matter of perspective. So the aliens might have no use for programming languages, or their languages might have no more built-in concept of "0-based arrays" than our languages have of "Happiness versus sadness".
Probably 0-based? I'd assume they'd come to the same reasoning as we did, that indexing into an offset starting at zero works better for addressing memory.
This would depend massively on the equally long evolutionary, cultural, and political history that they would have… just like that context is necessary to understand our own decisions. Folks assume “aliens” would be some kind of clean room, purely logical beings, but no such thing is possible and the assumption falls apart immediately.
And as someone who wants "programming" to NOT be the domain of "only programmers" I welcome this difficulty if it makes it easier for "regular people."
Let's humanize programming and not keep it in a tower.
This article mentions Janet as a possible "Lua replacement," which is unrealistic. It's hard to overcome Lua's inertia and familiarity.
But! If you're in the market for an embeddable-or-not scripting language, and you are turned off by Lua's various idiosyncrasies, I would encourage you to give Janet a closer look. It quickly became my favorite scripting language after I discovered it about a year ago.
It's great for embedding in a larger app -- it has a simple C API, and it's distributed as a single .h/.c file pair. It has a small standard library (the core composite types are immutable and mutable maps and vectors, rather than linked lists). Its first-class support for PEGs makes it a great choice for text-manipulation scripts. It can also compile statically-linked binaries (linking in the interpreter/runtime/gc) so it's easy to distribute Janet programs, unlike most interpreted languages. Binaries that only use the stdlib weigh about 1mb, if memory serves.
The language is sane: zero-indexed arrays, simple module/import system, variables are block-scoped by default (the language even distinguishes between bindings and reassignable variables). And it has non-hygienic "classic" lisp macros, so you can write helpers to do anything you can think of -- great for making a little embedded DSL.
No LuaJIT, though, and no metatables. And an extremely sparse package ecosystem. But it's pretty easy to interop with C/C++ libraries, so your "ecosystem" is actually broader than it looks (if you're willing to generate some bindings).
If it's a C program, I like to embed Chicken Scheme (alternatively, one could embed Gambit, or Guile). They are all pretty easy to embed with simple FFI and C APIs.
And before they complete the first trip, they're 0 years old, showing that you start from a count of 0. The one is the result from the first increment.
If I ask you to count the number of red balls in a bag with only 3 yellow balls, then the initial count in your head is 0, you inspect the balls one by one, never encountering a red ball, and thus never incrementing the count. And then you pronounce your final count of 0. So that's counting starting from 0.
What you call "starting at 1" is not so much the start as it is the first increment, which need not arise.
For arrays, the first element is the difference between a size 0 array and a size 1 array. So the choice between 0-indexed and 1-indexed is like the difference between pre-increment and post-increment. Do we associate the element with the array size up to the element, or to the array size up to and including the element? The choice seems arbitrary (personally I prefer using the same range of non-negative numbers for both, but language has a bias for associating "first" with index 1).
0 in normal usage is always a negation of something existing. You don't start counting things that exist at 0 (as you pointed out in your example of number of red balls in a bag that have none). However, position zero at an array in zero-indexed languages is the first position within the array.
There's no getting around the fact that higher level languages inherited 0 by counting byte offsets, rather than positions, even if those languages don't required that every index within an array have the same size in memory (or occupy a contiguous block of memory), and that this inherited usage is at odds with the actual plain-language and true meaning.
Totally irrelevant to the the actual conversation but I have in fact put my infant child’s age as 0 because I didn’t think to count in smaller increments than years. So there is at least one person who says babies are 0 years old.
That's only because age can easily be measured and described in units smaller than whole years, while array indexes can't be divided.
As for indexing from zero, it's best understood as indexing the position just before elements in an array. Where things will be inserted if you insert an element etc...
3/12 of a year
even after a year, they might say 18/12 of a year.
where does the first centimeter / inch start in a physical ruler ?
For the sequence of the ordinal numbers, whose purpose is to identify the position of the elements of an arbitrary sequence, any sequence of arbitrary symbols may be chosen and fixed by convention.
Most languages had at least in the beginning special words for the first and for the second elements of a sequence, without any relationship with the cardinal numbers. Even in English that remains true, even if "second" is a more recent substitution of the older word used previously. Many languages have special words for the last element and for the element before it. Some languages had special words for the third element and for the third element going backwards from the last. So in some languages it was possible to identify the elements of a 6-element sequence without using words derived from the cardinal numbers.
However inventing a very long sequence of words to be used as ordinal numbers, in order to identify positions in sequences with more than 2 to 6 elements, would have been difficult, so in most languages someone noticed that there already is a sequence of words that everybody had to memorize when learning how to count and which had rules for being extended to any length. So the ordinal numbers were derived by using a suffix or some other derivation rule from the cardinal numbers.
There is no logical reason for using 1 for the first ordinal position, this is just a historical accident.
The reason is that the children have always been taught to count by saying 1, 2, 3 and so on, instead of being taught to recite the sequence of the cardinal numbers from zero.
All the languages have always had a word for zero, but those words were normally created by applying a negation to words meaning "something", "one" or the like.
Because of this, the words for zero were not perceived as having an independent meaning and there was no need to learn them separately when the recitation of the cardinal numbers was learnt.
Nowadays we have a much better understanding of the meaning of the cardinal numbers and we are aware that 0 is a cardinal number like any other, so the children should really be taught to count 0, 1, 2, 3 ... and not 1, 2, 3, ... like 5000 years ago.
In the natural languages there is a huge inertia. Even if one would decide that since tomorrow the ordinal numbers should be 0th, 1th, 2th, 3th, 4th and so on, everybody would still have to know that whenever reading older writings the sequence of the ordinal numbers was 1st, 2nd, 3rd, 4th and so on, so a change of the convention to a more logical one would bring no simplification.
On the other hand, in the programming languages you can ignore the legacy conventions and choose the best conventions. Using for ordinal numbers the sequence 0, 1, 2, 3 ... is the best choice for many reasons, which have been explained in the literature many times, e.g. by Dijkstra.
Choosing to start the ordinal numbers from 1 in a programming language just demonstrates a lack of understanding of what the cardinal numbers and the ordinal numbers are and a lack of practical experience in programming and of understanding of how the programming language will be translated into machine language.
The first programming language for which I have studied the machine code generated by its compiler, when I was young, happened to be Fortran, which uses indices starting from 1. Until today I remember how I considered ugly and error prone all the tricks that the compiler was forced to use in order to avoid in many cases to make extra computations due to the poor choice of the origin of the indices.
Caring about 0 based or 1 based indexing is, to me, a sign of someone who struggles with programming in general, or is stuck doing a lot of finicky conversion between the two.
Most modern, higher level languages have generally abandoned using indexing, instead (even c++) they have something like:
"for x in y do z"
1-based indexing is a bit more readable, but in the end, you have hardly any benefit. There are better paradigms, algorithms, which don't require indexing at all, which IMO is the majority of what programmers are doing anyways. Even if you need to process two same-size collections at once (the majority of the remaining legitimate uses for index based looping), you are likely working pre-sorted data and should consider using a zip or pair which eliminates the need for managing indexes.
You say "but aren't you just imposing your own style on others"? Not really, if we want clean, minimal code, there should be as few references to the underlying architecture as possible, even the fact that we are dealing with a list is an implementation detail (is it actually a list, a linked list, a stream, a dictionary, an event, etc...), so in the context of implementing a higher-level language, this point is not only irrelevant, but shortsighted. If you are creating and looping over lists, you are likely not doing anything interesting, which is the whole point of programming in higher level languages, to do interesting things simply, right?
As for the remaining use case when we do actually want array based access, typically you find this in high-performance, architecture aware applications - then we actually want random memory access. We may even want to deal with explicit memory offset, which is what 0-based arrays are good at doing (often times the array is syntax sugar and we are literally assigning to/from pointer offsets).
To bring this around to my previous statement: "Caring about 0 based or 1 based indexing is, to me, a sign of someone who struggles with programming in general, or is stuck doing a lot of finicky conversion between the two."
The reason this comes up in the first place is that there is a divide. 0-based arrays are arguably much better for low level activities, and higher level languages generally left them in as a familiarity. Translating from 1 to 0 based arrays is not any easier than translating from 0 based to 1 based arrays, 1-based arrays provide just as much confusion and less familiarity in these cases. This is not a good thing.
Now I suppose Lua is trying to be something weird, a "high level" low-level language. Maybe that's fine, but it is weird, and people are right to be put-off by the change. If you are looping over bananas in lua, maybe you're not really using it the way it was intended, if you are doing memory-level access then its unnecessary language cruft.
Seems like a healthier and maybe even more productive way to see the world. I don't know enough about Korea to say whether that has anything at all to do with the way they count ages, but I feel like it's an interesting thought regardless.
"give me the 2nd item" --> a[2]
"how many items are in the array?" 5
"what is the index of the last item?" 5
I am used to 0-indexed and prefer consistency... yet there is an undeniable consistency to 1-indexed.
You're the person who brings back a dozen bottles of milk from the store when you see that they have eggs ;)
Usually, the more interesting question is:
What is the index of the next item (when extending the array)? Which is 5 in 0-based indexing.
What's closer to what programmers expect is a better choice. Being pedantic about "1 is actually better" helps no one.
It's the age old theory vs practice problem. Physicists vs engineers. Etc. This is why no one really cares about tau either.
Sorry, no. Humans count from 1. That's just a basic fact reflected in the history of numbers, which at early stages often didn't treat 0 as a number, but as a special case. And if you would say "I counted zero red balls" most people around will find it an unusual wording. Normal way of saying it doesn't involve mentioning 0 at all: "It's empty", "There are no red balls" etc.
1: https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/E...
"In corporate religions as in others, the heretic must be cast out not because of the probability that he is wrong but because of the possibility that he is right."
Also Julia's (default) 1-based indexing has become a mess.
Well, just like lua:
You count "things."
You don't count "not things."
Arrays are containers of things. Starting with 1 is overwhelmingly likely the thing that makes sense when using them.
Deleted Comment
It is a bit of a curse that every time this article ends up anywhere, the discussion quickly devolves onto the merits of 0-based indexing. I have a very strong opinion on this which is that "it doesn't matter". You use whatever paradigm is real in the language you're working. The context switch is not larger than the context switch between languages anyway. Lua is not the single 1-based indexing language out there, it used to be much more prevalent. I bet if JS had used 1-based indexing, half the people on this comment threads would just be praising the benefits of 1-based indexing. Sorry for the rant, but the reason I wrote that article in the first place is because I of such threads.
One thing I agree with many people here is that my baby example is not the best example. It is not wrong, but it is phrased in a confusing way. I'm tempted to edit the article and fix it or replace the example with something better. After all, the example is not important, it was just an example.
One aspect that many people don't realise, one that I couldn't really dive into in the article, is how simple and small the Lua source-code actually is. IIRC it is about 70 files of plain old C that can be compiled by any C99 compiler. That is very refreshing. The amount of power and flexibility you have with Lua when paired with the simplicity and maintainability of the Lua source-code itself is something we should all cherish and praise. Lua is small and nimble, it is small enough that you can vendor all of it in your project if you so want.
Anyway, I like 1-based indexing, they make my loops prettier. And yes, it was just a joke, I'm using iterators like everyone else and indexing hardly matter.
My favorite feature is the way that tables and closures allow you build up just as much of OOP as you need for the application. It feels very empowering. I'm working mostly solo, so I've been able to establish my own conventions and stick to them. Doing this as a large team would be fraught with peril and require strict standard practices to maintain an intelligible code base.
I was surprised the author didn't mention my #1 complaint, variables default to global scope AND initialized. If you misspell a variable name Lua will happily initialize it to nil and run with it. This almost always results in subtle incorrect behavior instead of an error.
i.e. The global scope is a table, and variables are keys on that table, so of course the value of any variable is `nil` because that's how tables work and Lua isn't going to special case a table just because it's a magic part of the environment.
...but, naturally, since the global environment is a table, you can set a metatable on it and require that variables be declared before you use them. :D
See: https://www.lua.org/pil/14.2.html
I think the design decision to have unresolved names result in defining a new variable, initialized to nil, is the result of a different design goal. I'm not sure what that goal was, something about fault tolerance maybe. In practice I find this feature means I need very thorough test coverage, or a metatable, as you've noted, or a static analyzer, as others have noted. Or a combination of all the above.
Setting __newindex on the global table to throw errors is a not-too-bad solution, but these will be run-time errors, so to catch them all you need good code coverage (yes, you want that anway) and uncouth third-party libraries can trigger those errors.
That python code throws UnboundLocalError, which seems like what it should do to me. If you move total = 0 to before def incr(x): then incr closes over the local total variable. Every call to make_counter returns a new counter with its own state.
The equivalent Lua defines total as a global (whether total = 0 is above or below). Multiple instances of incr are all changing the same global variable. That doesn't like the intended behavior.
https://www.php.net/manual/en/functions.anonymous.php
If your code editor doesn't warn you that you're potentially reading a variable before writing to it, then you need a new code editor.
Lua, originally, was a configuration language. You know, the sort of use-case you use a .ini (or these days, YAML) file for. Except you have realized you need some logic to set your flags, so you decide to make your config file programmable.
It has evolved to its current form but these roots remain. They should have added a 'strict' option.
1. Moonscript (https://moonscript.org/) the compile-to-lua language powering Itch.io, it's amazingly pleasant to work with and I was amazed porting a JavaScript tool to Moonscript that the moon version was more readable despite my lack of familiarity with the language.
2. Redbean (https://redbean.dev/) the single-file, cross-platform binary magic web server that's been all the rage lately. Super fun to embed Lua apps into a single binary and build small web apps.
3. Pico-8 (https://www.lexaloffle.com/pico-8.php) the famed "fantasy console" a fake old computer with fake old requirements that forces you to get creative as you write games and apps with the limited resources, of course, all apps for Pico-8 are written in Lua.
I think the moral here (especially with the last two projects listed) if you like when computers get weird you'll like Lua. I also recently compiled the Lua runtime into wasm to make some of my scripts portable to the browser, it was simple and easy, including modifying the language code just a bit.
Lua is a little wacky, but there's something so fun it brings me back to the early days of writing code when everything was simple and just a little off.
Currently working on developing a game in Minetest (https://www.minetest.net/), it is also scripted in Lua.
[1]: https://github.com/teal-language/tl
[2]: https://github.com/pallene-lang/pallene
https://github.com/Benjamin-Dobell/IntelliJ-Luanalysis/
Unfortunately, I haven't been able to give it as much attention as it deserves recently.
https://tic80.com/
Neovim [1] and WezTerm [2].
Neovim is the "hyperextensible Vim-based text editor”--quite the understatement IMHO.
WezTerm is an incredibly well done, GPU-accelerated cross-platform terminal written in Rust and uses Lua for configuration.
[1]: https://neovim.io
[2]: https://wezfurlong.org/wezterm/index.html
[1] https://awesomewm.org/
https://defold.com/
[1] https://cacm.acm.org/magazines/2018/11/232214-a-look-at-the-... [2] http://www.lua.org/doc/jucs05.pdf
Great way to put it. It's a very charming language. Once you learn the (very few) things that are off and can trip you up, it's really pleasant too.
https://spader.zone/firmament-1.0.0/
Lol, well that's a terrible point to make. When a baby is born, it's 0 zero years old. Human beings literally start at zero...
It doesn't matter much, because we will never reconcile the "I'm counting objects" and the "I'm pointing at addresses" (or the "I'm doing math… something something Dijkstra") people.
something which mathematics alone is not sufficient to uncover, it requires more foundations/logic to be relevant; it requires computer science (but focused as 'computology' and pure theory).
in mathematics this becomes more aparent with ring algebraic-structures; and when considering exponentials/logarithms/roots and powers.
a way to bring into focus what I'm trying to point out, is to consider the historical development of counting numbers. In a sense, it is not until the foundations are thoroughly considered (which is more logic than mathematics) that the importance of starting from zero really shines. In this 'historical sense' we started at one, and then went back and realized that it's more 'technically correct' to start at 0; this same foundational redefinition makes it important to note that zero is the only natural without a predecessor.
an alternative way to point at this 'deep and significant' issue, is to consider how zero is the additive identity and one is the multiplicative identity; algebraic-rings having both, and the distributive property being the axiom which links both. (I tend to focus on how both elements are identities; they're both instances of an 'identity' element; two sorts of identity).
> - In Korea, you are already one year old when you are born. > - In Korea, you "age" a year every New Year rather than on your birthday
https://www.omnicalculator.com/everyday-life/korean-age#:~:t....
However, if anyone went as far as learning about things like arrays indexed at 1, they are far more aware than the average software engineer. They actually spent time looking at it.
Lua is a victim of its own success. It's small(the runtime is tiny and can be made even smaller by shedding features), it's mostly uncontroversial, it does the job, it's fast. It can be easily sandboxed so you don't hear much about vulnerabilities. It's just there, invisible, in almost every modern computing device.
The games industry is an exception. They have readily embraced it, for very good reasons. Browsers should have used it from the get go, but that ship has sailed.
Next time you think about having scripting abilities in your application, consider Lua. Same way someone should consider Sqlite if they are contemplating creating a new file format for their application. It's a pretty good tool.
It's not about which convention is "right" and which is "wrong" — they are all conventions, after all. It's about context switching, and the additional cognitive load that a programmer coming from a language where arrays start at 0 will have. An entirely unnecessary chore.
Given that, no matter what, you're going to be stuck context switching, since C is zero-based. I wish Lua had picked 0-based indexing not because it's better, but because it's consistent with the host language Lua is designed to be embedded in.
So for this one use case at least, the 1-based indexing is pretty nice.
OT: which is why I think really great stuff like Racket (maybe even Haskell) have a hard time breaking through. The intended audience is people that have never seen a programming language, not tired engineers with deadlines.
If we discovered aliens and learned their programming languages, which is more likely: That they use 0-based arrays, or 1-based arrays?
We have assumed that aliens would do arithmetic, because we do arithmetic, and it seems very fundamental to us, but that might just be a matter of perspective. So the aliens might have no use for programming languages, or their languages might have no more built-in concept of "0-based arrays" than our languages have of "Happiness versus sadness".
Let's humanize programming and not keep it in a tower.
But! If you're in the market for an embeddable-or-not scripting language, and you are turned off by Lua's various idiosyncrasies, I would encourage you to give Janet a closer look. It quickly became my favorite scripting language after I discovered it about a year ago.
It's great for embedding in a larger app -- it has a simple C API, and it's distributed as a single .h/.c file pair. It has a small standard library (the core composite types are immutable and mutable maps and vectors, rather than linked lists). Its first-class support for PEGs makes it a great choice for text-manipulation scripts. It can also compile statically-linked binaries (linking in the interpreter/runtime/gc) so it's easy to distribute Janet programs, unlike most interpreted languages. Binaries that only use the stdlib weigh about 1mb, if memory serves.
The language is sane: zero-indexed arrays, simple module/import system, variables are block-scoped by default (the language even distinguishes between bindings and reassignable variables). And it has non-hygienic "classic" lisp macros, so you can write helpers to do anything you can think of -- great for making a little embedded DSL.
No LuaJIT, though, and no metatables. And an extremely sparse package ecosystem. But it's pretty easy to interop with C/C++ libraries, so your "ecosystem" is actually broader than it looks (if you're willing to generate some bindings).