Features I'll be glad to see in Lua 6+

406 views
Skip to first unread message

Martin Eden

unread,
Apr 5, 2025, 9:32:13 PMApr 5
to lu...@googlegroups.com
Hello list,

Here are some language features I wish Lua had already had.
Examples are from my use cases.

* "with" keyword

  with <table> do
    ...
  end

  Example:

    -- Before
    local Image
    do
      GradientGenerator.LineLength = Config.ImageWidth
      GradientGenerator.ColorFormat = Config.ColorFormat

      GradientGenerator:Run()

      Image = MakeImageFromLine(GradientGenerator.Line, Config.ImageHeight)
    end

    -- After
    local Image
    with GradientGenerator do
      LineLength = Config.ImageWidth
      ColorFormat = Config.ColorFormat

      Run(self) -- NB: "self", it's not keyword. Maybe ":Run()"?

      Image = MakeImageFromLine(Line, Config.ImageHeight)
    end

* "global" keyword, declarations are local by default

  global <var_list>

  Example:

    -- Before
    local RequestFunc = function() end
    _G.request = RequestFunc

    -- After
    RequestFunc = function() end
    global request = RequestFunc

  Main point is that I use "local" almost always. And "_G." prefix
  in rare cases when I consciously want global.

* "fail", "onfail" keywords

  General idea that any function can fail.

  Imagine that any function now returns new boolean value at first
  position. That value is eaten by interpreter. If it's false
  (function failed) then nearest in stack "atfail" block executes.

  Example:

    local Root =
      function(a, b, c)
        if (a == 0) then
          fail
        end

        return (-b + (b ^ 2 - 4 * a * c) ^ 0.5) / (2 * a)
      end

    local PrintRoot =
      function(a, b, c)
        print(('%.2f'):format(Root(a, b, c)))
      onfail
        print('Failed to determine root of equation.')
      end

    PrintRoot(2, -1, 0)
    PrintRoot('w', 't', 'f')
    PrintRoot(0, 0, 0)

  That's like "exception" but without overhead for type
  and additional data.

What do you guys would ask from language Santa? Another features,
some of these, nothing?

-- Martin

Jason Lethbridge

unread,
Apr 6, 2025, 12:42:23 AMApr 6
to lu...@googlegroups.com
> What do you guys would ask from language Santa? Another features,
> some of these, nothing?

On my wish list would be pragma directive equivalents and the ability
to use them to optionally define a values type to be less/more than 64-
bits and even treated as unsigned. It would not require a new version
of Lua to implement, just an aware interpreter and a standard all
supporting interpreters agree on.

Example:
my_byte = 255 --#type u8
my_port = 8080 --#type u16
my_dword = -1 --#type i32
my_float = 3.14 --#type f32

An unaware interpreter would see `--` as a comment and not process
further. Leaving each value as a double/i64 and allowing the script to
be fully backwards compatible.

A aware interpreter would see `--#` and treat all characters until the
end of the line like a `#pragma` directive in C. If the interpreter
doesn't understand the statement it would ignore it which would allow
new or niche directive statements to be invented and used in the future
while keeping the script itself as backwards compatible as possible.

Example:

decimal = 3.14 --#type f128 -- A special interpreter for 128-bit RISC-V

for i = 0, -1 do --#type u128 --#omp parallel for -- OpenMP loop
decimal = decimal * i --#type f128 -- Continue to be a f128
print(decimal)
end

bil til

unread,
Apr 6, 2025, 4:07:26 AMApr 6
to lu...@googlegroups.com
Am So., 6. Apr. 2025 um 03:32 Uhr schrieb 'Martin Eden' via lua-l
<lu...@googlegroups.com>:

> Here are some language features I wish Lua had already had.
> * "fail", "onfail" keywords
>
> Imagine that any function now returns new boolean value at first
> position. That value is eaten by interpreter. If it's false
> (function failed) then nearest in stack "atfail" block executes.
>
Thanks for your initiative to proceed with V6.

As in part of my projects I am very interested to implement Lua into
IoT projects with ARM M4 controllers 32bit, I would be VERY
interested, that the binary files of lua should align align all their
code (possibly also all constant data) on 32bit adresses (versa file
start / start of first function code). So that it is NO more necessary
to load pre-compiled bin files into the RAM and such spoil "valuable"
RAM space (which in single chip controllers typically is SRAM with
quite large memory cells, typically the RAM memory is really
relatively restricted in such controllers.). Instead ARM M4 e. g. in
STM32 controllers works perfectly by accessing any constant data
directly from ROM, but it MUST be 32bit aligned for such direct ROM
access.

Concerning this "atfail" proposed by you: In my view this sounds a bit
lke a "somehow nice dream" for "lazy error programming" ... . Just
please understand that "nice" and "lazy" dreams also often are also
"dangerous" dreams... . I think e. g. if you would implement this, you
would get severe problems with any mulitasking / task-yielding
concepts, also with cooperative mulittasking which is really
implemented very nicely in Lua and used by VERY many applications.

To get this done in your software in a more safe way, you could as
well set a global variable "ErrorOccured" to some value in any error
case... . "Error programming" typically should be VERY fail safe, if
it is presented by a more "basic operating system" like Lua, as most
programmers do not like to think too much about error programming /
they just use such proposed things without testing it very much, and
errors happen relatively seldom typically ... so there is quite a
danger, that IF such error answering is programmed in ANY way lazy /
non-clean, the failure implications will be seldom but VERY terrible
then.

Marc Chantreux

unread,
Apr 6, 2025, 7:17:51 AMApr 6
to 'Martin Eden' via lua-l
On Sun, Apr 06, 2025 at 03:32:02AM +0200, 'Martin Eden' via lua-l wrote:
> Here are some language features I wish Lua had already had.
> Examples are from my use cases.
>

>     -- Before
>     do
>       GradientGenerator.LineLength = Config.ImageWidth
>       GradientGenerator.ColorFormat = Config.ColorFormat
>       GradientGenerator:Run()
>       Image = MakeImageFromLine(GradientGenerator.Line, Config.ImageHeight)
>     end

>     -- After
>     local Image
>     with GradientGenerator do
>       LineLength = Config.ImageWidth
>       ColorFormat = Config.ColorFormat
>       Run(self) -- NB: "self", it's not keyword. Maybe ":Run()"?
>       Image = MakeImageFromLine(Line, Config.ImageHeight)
>     end

so how do I know Image isn't GradientGenerator.Image and how the
implementor know that it's :Run, not run?

please consider the raku (also jq) way to be explicitly implicit by
autorizing the calling operator on nothing.

so we got

with GradientGenerator do
.LineLength = Config.ImageWidth
.ColorFormat = Config.ColorFormat
:Run()
Image = MakeImageFromLine(Line, Config.ImageHeight)
end

would be also super nice to remove the parentheses for calling
and the algol syntax for control statement (by indenting or pairing)

and a block should return.

put it all together and this is the lua I dream about:

Image = with GradientGenerator {
.LineLength = Config.ImageWidth
.ColorFormat = Config.ColorFormat
:Run
MakeImageFromLine Line, Config.ImageHeight
}

But I'm not a langage implementor so imagine lua is a boring langage for
the most important reason of its adoption: simple codebase with low
footprint. that's why I use https://fennel-lang.org/ and just consider
lua as an IR.

regards

--
Marc Chantreux

Frityet

unread,
Apr 6, 2025, 7:42:22 AMApr 6
to lu...@googlegroups.com
It would have to be type annotations that are ignored by the interpreter, just so tooling could be made awesome

> On Apr 6, 2025, at 12:17, Marc Chantreux <m...@unistra.fr> wrote:
> --
> You received this message because you are subscribed to the Google Groups "lua-l" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to lua-l+un...@googlegroups.com.
> To view this discussion visit https://groups.google.com/d/msgid/lua-l/Z_Ji2IugrbHoiRX2%40prometheus.

Scott Morgan

unread,
Apr 6, 2025, 9:24:10 AMApr 6
to lu...@googlegroups.com
On 06/04/2025 02:32, 'Martin Eden' via lua-l wrote:
> What do you guys would ask from language Santa? Another features,
> some of these, nothing?

1) nil safe table indexing, so you can grab values that may or may not
be present in nestled table structures:

a = { b = { c = { d = 123 } } }
print(a.b.c.d) -- > 123
print(a.b.x.d) -- > Error!
print(a?b?c?d) -- > 123
print(a?b?x?d) -- > nil

There's various ways around it currently, but they all have drawbacks.


2) I really miss C's += operator, which could include table concatenation:

x = 10
x += 5 --> x == 15

tab = { 1, 2 }
tab += 3 --> { 1, 2, 3 }

Thinking about it; if you allowed .. to work on a table:

tab = { 1, 2 } .. 3 --> { 1, 2, 3 }
tab = { 1, 2 } .. { 3, 4 } --> { 1, 2, 3, 4 }

This would have the advantage of making ropes easier to implement, to
the extent you could get away with a single rope_to_string function,
rather than a whole class in a lot of cases.

Then you could have ..= as simple syntax sugar for both strings and
tables, leaving += (and the other operators, I suppose) just for sugar
on numbers:

str = "Hello, "
str ..= "World!" -- > "Hello, World!"


3) Bring back table.foreach!!

Yeah, it's easy to implement, but it's such a PITA when you're sitting
at some random Lua instance and just want to quickly look into some
data. Just put it back in the standard lib.

Maybe an enhanced version that can traverse a whole tree, without
getting stuck in loops.


I can dream :)

Scott

Spar

unread,
Apr 6, 2025, 10:05:49 AMApr 6
to lu...@googlegroups.com
Any thoughts on single built in class system?
--
You received this message because you are subscribed to the Google Groups "lua-l" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lua-l+un...@googlegroups.com.

Yao Zi

unread,
Apr 6, 2025, 12:49:04 PMApr 6
to 'Scott Morgan' via lua-l
On Sun, Apr 06, 2025 at 02:24:05PM +0100, 'Scott Morgan' via lua-l wrote:
> On 06/04/2025 02:32, 'Martin Eden' via lua-l wrote:
> > What do you guys would ask from language Santa? Another features,
> > some of these, nothing?
>
> 1) nil safe table indexing, so you can grab values that may or may not be
> present in nestled table structures:
>
> a = { b = { c = { d = 123 } } }
> print(a.b.c.d) -- > 123
> print(a.b.x.d) -- > Error!
> print(a?b?c?d) -- > 123
> print(a?b?x?d) -- > nil
>
> There's various ways around it currently, but they all have drawbacks.

Will second this and it may be even implemented with only some frontend
changes.

> 2) I really miss C's += operator, which could include table concatenation:
>
> x = 10
> x += 5 --> x == 15
>
> tab = { 1, 2 }
> tab += 3 --> { 1, 2, 3 }
>
> Thinking about it; if you allowed .. to work on a table:
>
> tab = { 1, 2 } .. 3 --> { 1, 2, 3 }
> tab = { 1, 2 } .. { 3, 4 } --> { 1, 2, 3, 4 }
>
> This would have the advantage of making ropes easier to implement, to the
> extent you could get away with a single rope_to_string function, rather than
> a whole class in a lot of cases.

But I don't think it's a good idea: this hides the fact that concating
two tables is probably expensive. And may come with some uneasy choices,

1. After the operation, what state the original table ("{1, 2}" and
"{3, 4}" in your case) should be in? Should it be modified in place,
or this operation creates a new object?
- Modifying inputs in place sounds a bad idea to me, this is often
misleading and causes bugs, not to mention no assignment may be
involved in the case.
- Creating new objects obviously brings a large overhead, just like
string concating. This will stops anyone who wants an efficient
concating to make use of the feature.

2. table concatng is much more complex than the string one. IMHO it's
hard to desugar and we may end up adding an extra instruction to our
VM.

3. What should the operation do on tables with holes? i.e. what's the
result of { [1] = 1, [3] = 3 } .. { [2] = 1 }? How should it behave
on dictionary-like tables ({ foo = 1 } .. { bar = 2 }) or mixed ones?

IMHO the only reason to get Lua implementing table "concating" in C is
performance. If it's really an issue, I'll propose to introduce
table.merge(),

1. This function modifies passed table in place, just like
table.insert().
2. Problem 2 doesn't exist anymore if we implement the functionality
through a library subrountine.
3. table.merge() appends the later table's numeric part to the former
one's. The hashtable (dictionary) parts are simply merged, and the
later one overrides the former one if there're duplicated entires.

Anyway, you could always implement the syntax with

local concatable = { __concat = function(a, b)
local r = {};

for i, v in ipairs(a) do
r[i] = v;
end

for i, v in ipairs(b) do
r[i + #a] = v;
end

return r;
end };

local function
makeConcatable(t)
return setmetatable(t, concatable);
end

local a, b = makeConcatable{ 1, 2 }, makeConcatable{ 3, 4 };
for _, v in ipairs(a .. b) do
print(v);
end

although I think it's really not a good idea.

> Then you could have ..= as simple syntax sugar for both strings and tables,
> leaving += (and the other operators, I suppose) just for sugar on numbers:
>
> str = "Hello, "
> str ..= "World!" -- > "Hello, World!"
>
>
> 3) Bring back table.foreach!!
>
> Yeah, it's easy to implement, but it's such a PITA when you're sitting at
> some random Lua instance and just want to quickly look into some data. Just
> put it back in the standard lib.
>
> Maybe an enhanced version that can traverse a whole tree, without getting
> stuck in loops.
>
>
> I can dream :)
>

Thanks,
Yao Zi

Yao Zi

unread,
Apr 6, 2025, 1:32:41 PMApr 6
to 'Martin Eden' via lua-l
It seems also an example that do-end blocks evaluating to a value, like
the "begin" block in Lisp, work well,

local Image = do
local g = NewGradientGenerator;
g.LineLength = Config.ImageWidth;
g.ColorFormat = Config.ColorFormat;

g:Run();

MakeImageFromLine(Line, Config.ImageHeight);
end;

This should be a frontend-only change and will help manage scope of
variables. I'm not sure the influence to Lua's LL1 grammar though.

> * "global" keyword, declarations are local by default
>
>   global <var_list>
>
>   Example:
>
>     -- Before
>     local RequestFunc = function() end
>     _G.request = RequestFunc
>
>     -- After
>     RequestFunc = function() end
>     global request = RequestFunc
>
>   Main point is that I use "local" almost always. And "_G." prefix
>   in rare cases when I consciously want global.

It may be too late to change... locals in every place have already been
my impression of Lua and it will look strange to me if it's unnecessary
to write "local"s anymore...

Anyway, I definitely second the idea.

> * "fail", "onfail" keywords
>
>   General idea that any function can fail.
>
>   Imagine that any function now returns new boolean value at first
>   position. That value is eaten by interpreter. If it's false
>   (function failed) then nearest in stack "atfail" block executes.
>
>   Example:
>
>     local Root =
>       function(a, b, c)
>         if (a == 0) then
>           fail
>         end
>
>         return (-b + (b ^ 2 - 4 * a * c) ^ 0.5) / (2 * a)
>       end
>
>     local PrintRoot =
>       function(a, b, c)
>         print(('%.2f'):format(Root(a, b, c)))
>       onfail
>         print('Failed to determine root of equation.')
>       end
>
>     PrintRoot(2, -1, 0)
>     PrintRoot('w', 't', 'f')
>     PrintRoot(0, 0, 0)
>
>   That's like "exception" but without overhead for type
>   and additional data.

This is something like Haskell's Maybe monad. It doesn't come without
any overhead: it's just hidden by the interpreter.

>   Imagine that any function now returns new boolean value at first
>   position. That value is eaten by interpreter.

Why not just introduce a real "catch" to Lua that makes use of our
current infra of error() and pcall()?

And this strategy of error handling cannot carry and propagate error
information and stack backtrace, thus it looks incomplete to me.

> What do you guys would ask from language Santa? Another features,
> some of these, nothing?

Besides blocks evaluating to a value, I'd like to see "function" keyword
get shorter, like "fn" or at least "func", which helps FP-style
programming in Lua :)

> -- Martin

Regards,
Yao Zi

Martin Eden

unread,
Apr 6, 2025, 6:18:16 PMApr 6
to lu...@googlegroups.com

On 2025-04-06 19:32, 'Yao Zi' via lua-l wrote:
> And this strategy of error handling cannot carry and propagate error
> information and stack backtrace, thus it looks incomplete to me.
With "fail", "onfail" keywords I propose happy-path programming.
Not crash investigation.

So code doing something weird that sometimes works will have

  onfail
    print('Something went wrong :(') -- (c)

Because as me-user I don't see any value in stack trace (and memory dump
and log file) from crashed program. For me something went wrong.
I don't care what. I'm not gonna fix program code. I'm not gonna send
crash reports to developer.

As me-coder I'm using print() to trace, test and debug. What is required
to fix crash is reproducible case. Stack trace, memory dump, log and
print() are just temporary tools while mangling guts of code.

In more idealistic world I would imagine "fail" as global function.
So for crash investigation I can override it to print location
in source code where "fail" was called.

-- Martin

Martin Eden

unread,
Apr 6, 2025, 6:44:22 PMApr 6
to lu...@googlegroups.com

On 2025-04-06 13:17, Marc Chantreux wrote:
> Image = with GradientGenerator {
> .LineLength = Config.ImageWidth
> .ColorFormat = Config.ColorFormat
> :Run
> MakeImageFromLine Line, Config.ImageHeight
> }
If blocks can return values I still prefer to keep "return" keyword.
Or else it's not clear what is result of block: result of ":Run()"
or "MakeImageFromLine()". Or all of them? (Merging block results is
for real, look at Bash and PowerShell.)

Also I believe we do need parenthesis (or brackets, or braces or
do-ends) for grouping.

Then code can also be written as

  -- Braces instead of do-ends
  local Image =
    MakeImageFromLine(
      with GradientGenerator {
        .LineLength = Config.ImageWidth
        .ColorFormat = Config.ColorFormat
        :Run
        return .Line
      },
      Config.ImageHeight
    )

We can drop commas, it will remain parseable but will become less
readable.

-- Martin

Martin Eden

unread,
Apr 6, 2025, 6:54:06 PMApr 6
to lu...@googlegroups.com
On 2025-04-06 19:32, 'Yao Zi' via lua-l wrote:
> It seems also an example that do-end blocks evaluating to a value, like
> the "begin" block in Lisp, work well,
>
> local Image = do
> local g = NewGradientGenerator;
> g.LineLength = Config.ImageWidth;
> g.ColorFormat = Config.ColorFormat;
>
> g:Run();
>
> MakeImageFromLine(Line, Config.ImageHeight);
> end;

"return" is still needed. (Also it's "g.Line".)

> Besides blocks evaluating to a value
For me that's about making any closure returnable.

Currently we have these types of closures:

  * source code file - can return
  * "function" - can return
  * "for", "while" - can't return
  * "do end" block - can't return

I think that any closure is capable of returning values.

"with <table> do end" construction proposal is about new type of
closure, leaving returnability perk unspecified.

-- Martin

Martin Eden

unread,
Apr 7, 2025, 1:51:44 AMApr 7
to lu...@googlegroups.com

On 2025-04-06 03:32, 'Martin Eden' via lua-l wrote:
> * "with" keyword
>
>   with <table> do
>     ...
>   end

After more thinking I'm dismissing "with" closure proposal.

Problem is that you never know what's inside table. It can be generated
randomly. So sometimes code works, sometimes upvalue is shadowed by
table field:

  local Data
  with RandomTable do
    Data = GetData()
    -- ^ <Data> can occur to be field in <RandomTable>
  end

Ambiguity can be resolved by allowing headless qualifiers (".Data"),
but you can always create one-character alias without any language
changes:

  local R = RandomTable
  local Data
  do
    Data = R.GetData()
  end

-- Martin

bil til

unread,
Apr 7, 2025, 2:20:13 AMApr 7
to lu...@googlegroups.com
Am So., 6. Apr. 2025 um 03:32 Uhr schrieb 'Martin Eden' via lua-l
<lu...@googlegroups.com>:
>
> Here are some language features I wish Lua had already had.

... one further point, but I think the Lua people have this "on the
screen" already:

local counter variables in numerical for loops should please behave
like local variables in (all?) other languages:

It should be no problem to modify the counter variable during the for loop.

So the warning "You should not change the value of the control
variable during the loop" in RefMan 3.3.5 should be "stripped".

Francisco Olarte

unread,
Apr 7, 2025, 3:36:10 AMApr 7
to lu...@googlegroups.com
On Mon, 7 Apr 2025 at 08:20, bil til <bilt...@gmail.com> wrote:
...
> ... one further point, but I think the Lua people have this "on the
> screen" already:
> local counter variables in numerical for loops should please behave
> like local variables in (all?) other languages:

Which ones? I mean, which languages WITH NUMERIC LOOPS let you freely
modify the "counter" without problem? ( i.e., python has no numeric
for, perl/c/Java have no numeric for ( for(;;) is not numeric, numeric
usage is by convention ). My fortran is rusty ( and mainly 77 ) but
DOs where scary enough without touching the index.

> It should be no problem to modify the counter variable during the for loop.
> So the warning "You should not change the value of the control
> variable during the loop" in RefMan 3.3.5 should be "stripped".

And do you see great usage in that? I can think of a cople, but they
are not that simple.

Maybe what you want, and what I would ask for, is just a C++ style
generic for(;;) loop ( including declaring locals in the first
statement ), which will mainly be sugar, but useful sugar IMO. But
fitting it in the language may be difficult, especially given "for" is
currently used for two things and calling it by another name may
introduce more confusion than help for programmers using several
languages., but if I'm not confused "for" + "(" is currently up for
grabs.
This, besides the pure sugar op= stuff ( maybe with some optimization
similar to what :call does ). is what I think would benefit my code
more, op= to avoid typos ( and maybe accelerate a bit things like
a.b.c.d.e.f=a.b.c.d.e.f+1, which I normally allways do to avoid both a
typo oportunity and an indexing chain surprise ( I have modifying
__index functions around, they would not do damage there but make me
wary of these things ) )

Francisco Olarte.

Sean Conner

unread,
Apr 7, 2025, 4:24:46 AMApr 7
to lu...@googlegroups.com
It was thus said that the Great Francisco Olarte once stated:
> On Mon, 7 Apr 2025 at 08:20, bil til <bilt...@gmail.com> wrote:
> ...
> > ... one further point, but I think the Lua people have this "on the
> > screen" already:
> > local counter variables in numerical for loops should please behave
> > like local variables in (all?) other languages:
>
> Which ones? I mean, which languages WITH NUMERIC LOOPS let you freely
> modify the "counter" without problem? ( i.e., python has no numeric
> for, perl/c/Java have no numeric for ( for(;;) is not numeric, numeric
> usage is by convention ). My fortran is rusty ( and mainly 77 ) but
> DOs where scary enough without touching the index.

Microsoft BASIC for 8-bit computers. Just ran this bit of code on the
Tandy Color Computer, using Disk Extended Color Basic 1.1, copyright 1982 by
Tandy, under licence from Microsoft:

10 FOR X=1 TO 100 STEP 1
11 PRINT X
12 X=X+10
13 NEXT X

and got the following output:

1
12
23
34
45
56
67
78
89
100

-spc (So there's one with only a numeric FOR loop ... )

Marc Chantreux

unread,
Apr 7, 2025, 4:30:33 AMApr 7
to 'Martin Eden' via lua-l
hello,

On Mon, Apr 07, 2025 at 12:44:10AM +0200, 'Martin Eden' via lua-l wrote:
> On 2025-04-06 13:17, Marc Chantreux wrote:
> > Image = with GradientGenerator {
> > .LineLength = Config.ImageWidth
> > .ColorFormat = Config.ColorFormat
> > :Run
> > MakeImageFromLine Line, Config.ImageHeight
> > }
> If blocks can return values I still prefer to keep "return" keyword.
> Or else it's not clear what is result of block: result of ":Run()"
> or "MakeImageFromLine()". Or all of them? (Merging block results is
> for real, look at Bash and PowerShell.)

you mentionned bash so you are used to it:

if stage1
then$
echo -n stage 2 returns
stage3
else
echo -n shutdown and return
emergency_procedure
fi
echo $?

but you get it in perl:

do {
if ( $a ) { b; c }
else { d }
}

$a ? do { b; c } : d;

schemes:

(cond
( a (do (b) (c)))
(else d))

lot of langages will lambdas (python, js)

a = () => console.log("hello")
b = (x) => x + 1
odd = (x) => x % 2

you also have it it rust and C AFAIR.

> Also I believe we do need parenthesis (or brackets, or braces or
> do-ends) for grouping

*for grouping* ! I agree totally. so this


say (1 + 1), f 4

or even

say (1 + 1), (f 4)

is grouping

but

say( (1+1), (f(4)))

is grouping and crypting with extra noisy
parentheses.

> Then code can also be written as
>   -- Braces instead of do-ends
>   local Image =
>     MakeImageFromLine(
>       with GradientGenerator {
>         .LineLength = Config.ImageWidth
>         .ColorFormat = Config.ColorFormat
>         :Run
>         return .Line
>       },
>       Config.ImageHeight
>     )
>

is Config.ImageHeight related to the
computation of Image ? If not you shouldn't
write this code.

if it is, the way I understand your code is
Config.ImageHeight will never be reached. What
you need there is the defer keyword (you have it
in go, zig, raku, shell (with trap code EXIT) …)

> We can drop commas, it will remain parseable but will become less
> readable.

commas are not the worse problem but yes, it could be nice.

--
Marc Chantreux

Francisco Olarte

unread,
Apr 7, 2025, 4:42:42 AMApr 7
to lu...@googlegroups.com
On Mon, 7 Apr 2025 at 10:24, Sean Conner <se...@conman.org> wrote:
> It was thus said that the Great Francisco Olarte once stated:
...
> > Which ones? I mean, which languages WITH NUMERIC LOOPS let you freely
> > modify the "counter" without problem? ( i.e., python has no numeric
...
> Microsoft BASIC for 8-bit computers. Just ran this bit of code on the
> Tandy Color Computer, using Disk Extended Color Basic 1.1, copyright 1982 by
> Tandy, under licence from Microsoft:

I haven't used that one, since the TRS80, mid 80s, days. I did amstrad
basic in the mid-late 80s and hazily remebered you could do nearly
anything.
...
> -spc (So there's one with only a numeric FOR loop ... )

Yep, and I think f66 ( with do loops the way god meant them to be ;->
) was similar. I still have some pamphlets for it showing a convention
for doing all the classic control flow with gotos.

Francisco Olarte.

bil til

unread,
Apr 7, 2025, 4:47:34 AMApr 7
to lu...@googlegroups.com
>Microsoft BASIC for 8-bit computers. Just ran this bit of code on the
>Tandy Color Computer, using Disk Extended Color Basic 1.1, copyright 1982 by
>Tandy, under licence from Microsoft:

Thanks for antique example :) ... this you can really do in most
languages I think.

(I do not know Phyton, C very well, Basic also, Java also - I think
all support this, the "normal programming user" is somehow just used
that this would work...).

(We had this discussion already 1 year ago, it should be only small
change in Lua code as I see it ... as far as I remember at this time
you / Roberto agreed that this should be a minor change but very
useful for "standard users").

Martin Eden

unread,
Apr 7, 2025, 12:57:13 PMApr 7
to lu...@googlegroups.com
On 2025-04-07 08:19, bil til wrote:
> local counter variables in numerical for loops should please behave
> like local variables in (all?) other languages:
>
> It should be no problem to modify the counter variable during the for loop.
Second that

My typical use case would be iterating sequence with possible
forward jumps.

Say, you want to print unescaped string contents (\-escapes):

  for i = 1, #s do
    local c = s:sub(i, i)
    if (c == [[\]]) then
      i = i + 1
      c = s:sub(i, i)
    end
    io.write(c)
  end

-- Martin

Martin Eden

unread,
Apr 7, 2025, 1:47:49 PMApr 7
to lu...@googlegroups.com
Hello Marc,

On 2025-04-07 10:30, Marc Chantreux wrote:
> say( (1+1), (f(4)))
>
> is grouping and crypting with extra noisy
> parentheses.

Then how you will interpret "say say": "say(say)" or "say() say()"?


> you mentionned bash so you are used to it:
>
> if stage1
> then$
> echo -n stage 2 returns
> stage3
> else
> echo -n shutdown and return
> emergency_procedure
> fi
> echo $?
>
> but you get it in perl:
>
> do {
> if ( $a ) { b; c }
> else { d }
> }
>
> $a ? do { b; c } : d;
>
> schemes:
>
> (cond
> ( a (do (b) (c)))
> (else d))
>
> lot of langages will lambdas (python, js)
>
> a = () => console.log("hello")
> b = (x) => x + 1
> odd = (x) => x % 2
>
> you also have it it rust and C AFAIR.

I didn't understand most of these snippets. But looks like you
demonstrating anonymous code blocks.

My question was like "what is return value of code block"?
In bash and PowerShell iirc there is no return statement.
All output from function call can be captured. This becomes a hell
when your function is expected to return one word and you need to
call chatty tools inside that function. That's why they use those
weird redirects to /dev/null.


On 2025-04-07 10:30, Marc Chantreux wrote:
>> Then code can also be written as
>>   -- Braces instead of do-ends
>>   local Image =
>>     MakeImageFromLine(
>>       with GradientGenerator {
>>         .LineLength = Config.ImageWidth
>>         .ColorFormat = Config.ColorFormat
>>         :Run
>>         return .Line
>>       },
>>       Config.ImageHeight
>>     )

> is Config.ImageHeight related to the
> computation of Image ? If not you shouldn't
> write this code.
>
> if it is, the way I understand your code is
> Config.ImageHeight will never be reached.
"Config.ImageHeight" here is second argument for MakeImageFromLine().

Quoted snippet is imaginary example how code may look with blocks
returning values. Another variant is make block around MakeImageFromLine().
You wrote those variant in your post.

Real code on which this example is based:

https://github.com/martin-eden/Lua-LinearPlasmGenerator/blob/de54c3c3a7ee2b141ffdfd2823155a24160f456f/Plasm_1d_ppm.lua#L65

-- Martin

Marc Chantreux

unread,
Apr 7, 2025, 5:26:43 PMApr 7
to 'Martin Eden' via lua-l
On Mon, Apr 07, 2025 at 07:47:39PM +0200, 'Martin Eden' via lua-l wrote:
> > is grouping and crypting with extra noisy
> > parentheses.
>
> Then how you will interpret "say say": "say(say)" or "say() say()"?

say(say())

that's why commas are important in those langages because

say 3, f, 3; => say(3, f, 3);

when

say 3, f 3; => say(3, f(3));

> My question was like "what is return value of code block"?
> In bash and PowerShell iirc there is no return statement.

yeah but in bash, you can't return for real. just setting $?
which will be used by control statement.

implicit return is more: any line can return by default. to make
my example more explicit: all those f are identical:

sub f($x) { $x <= 10 }
sub f($x) { return $x <= 10 }

sub f($x) {
if ($x <= 10 ) { true }
else { false }
}

sub f($x) {
if ($x <= 10 ) { return true }
else { return false }
}


> All output from function call can be captured. This becomes a hell
> when your function is expected to return one word and you need to
> call chatty tools inside that function.

> That's why they use those
> weird redirects to /dev/null.

no: redirect to is capturing a yield, not a return!

a process can yield (write) many bytes but *returns* a single
status value (from 0 to 255 AFAIR).

regards,

--
Marc Chantreux

sur-behoffski

unread,
Apr 7, 2025, 10:39:29 PMApr 7
to lu...@googlegroups.com
G'day,

Just in passing: A design philosophy recommended to me by a Wise
Person (especially in the 80s arena of software for 8-bit
microprocessors):

Start with the most restrictive policy for ["whatever"]; and only
reluctantly relax constraints when the policy is found to be
unworkably tight.

It's far, far easier to move from tight constraints to more
relaxed constraints; if policies are loose up front, and you want
to tighten things down the track, usually it's much harder to do.

So, I tend to approach suggested new features with a lot of
caution. It'd be good if the proposer could show a relevant use
case, where the feature improves on the existing baseline.
This would be harder than it looks, given that the proposer might
be coming from some development angle, and the overall Lua
community is a "broad, diverse church".

----------------------------------------

(The one C idiom I'd love to see in Lua is the "continue"
statement, and even support it as an optional patch in my
lglicua project (with many thanks to the original author). This
is because of a related design philosophy: I hate having deep
levels of nesting, and so force 8-column indent (spaces only, no
tabs), so deep nesting quickly shows itself as code that needs
to be refactored. I like writing, e.g.:

```
for (i = 1; i <=10; i++) {
/* Some code, relevant for all items. */
/* [...] */

/* Does this item have no children? */
if ((G[i]->Left == NULL) && (G[i]->Right == NULL)) {
/* Yes, skip irrelevant processing. */
continue;
}

/* More processing of items. */
/* [...] */
}
```

(Incidentally, the no-tabs-in-preceding-whitespace
requirement, along with no-trailing whitespace requirement,
where applicable, is intended to normalise whitespace, so
content-hash-based version control systems, such as Git,
always sees a consistent, "canonical" view of source code.)

[Tools, such as Makefiles, that require tab characters, are
of course exempt from the leading-spaces rule.]

cheers,

sur-behoffski (Brenton Hoff)
programmer, Grouse Software

Mitchell

unread,
Apr 8, 2025, 1:34:20 AMApr 8
to lu...@googlegroups.com
Hi,

> On Apr 7, 2025, at 10:39 PM, sur-behoffski <sur-be...@grouse.com.au> wrote:
>
> <snip>
>
> (The one C idiom I'd love to see in Lua is the "continue"
> statement, and even support it as an optional patch in my
> lglicua project (with many thanks to the original author). This
> is because of a related design philosophy: I hate having deep
> levels of nesting, and so force 8-column indent (spaces only, no
> tabs), so deep nesting quickly shows itself as code that needs
> to be refactored.

For this very reason I like to use “goto continue”:

for k, v in pairs(t) do
if not condition(k) then goto continue end
-- do something with v
::continue::
done

Some would argue goto is harmful, but it has its place in clean code.

Cheers,
Mitchell

Marc Chantreux

unread,
Apr 8, 2025, 2:15:10 AMApr 8
to lu...@googlegroups.com
On Mon, Apr 07, 2025 at 11:44:25PM -0400, Mitchell wrote:
> Some would argue goto is harmful, but it has its place in clean code.

Which is such an ignorant claim! "abuse of goto" actually is
( like alias and non-quoting in shell)

* you should avoid it as long as you can
* sometimes you feel the need of it because it's the best way
to make the code clean
* then you're really happy to see that the feature is indeed
supported.

regards

--
Marc Chantreux

Frank Dana (FeRD)

unread,
Apr 8, 2025, 3:20:27 PMApr 8
to lua-l
On Tuesday, April 8, 2025 at 1:34:20 AM UTC-4 Mitchell wrote:
Hi,

> On Apr 7, 2025, at 10:39 PM, sur-behoffski wrote:
>
> <snip>
>
> (The one C idiom I'd love to see in Lua is the "continue"
> statement, 

For this very reason I like to use “goto continue”:

for k, v in pairs(t) do
if not condition(k) then goto continue end
-- do something with v
::continue::
done

Some would argue goto is harmful, but it has its place in clean code.


In that particular case, though, I'd argue that while the goto may not be harmful, it's merely a kludge to work around the lack of a 'continue' statement. (As well as a justification for its inclusion in the language!) 

With a real 'continue' you wouldn't need the goto, or the label, yet for all that brevity the code would still be easier to follow. You wouldn't have to find the '::continue::' label (and the longer the loop body, the more work that entails), just to determine that nothing happens as a result of the goto except advancing to the next loop iteration. (All of which is immediately implicit in a real 'continue' statement, but without requiring any additional analysis.)

Mitchell

unread,
Apr 8, 2025, 3:42:53 PMApr 8
to lu...@googlegroups.com
Hi,
I agree with you that a single “continue” is shorter and more obvious in intent than “goto continue” in this one case. However, I’ve come across more than one way to effectively use “goto” other than “goto continue”. (I’m sure others have too.) Therefore, from a numbers perspective, I’ll take “goto” over “continue” because the latter is good for *exactly* one use case, whereas the former is good for *at least* one use case.

Cheers,
Mitchell

Warner Losh

unread,
Apr 8, 2025, 4:16:00 PMApr 8
to lu...@googlegroups.com
Continue is a specialized goto. There's only one place it can go. Either to the next iterator / termination part of the loop in it's no arg form, or to some well defined other loop instance of that with an arg (at least in other languages). You get used to it.

Goto next is an easy workaround in lua for the lack. It is a more powerful tool that can solve other problems (like needing to retry something), but that power can be abused. There's much debate about how big this problem is in practice.. 

Warner


Cheers,
Mitchell


--
You received this message because you are subscribed to the Google Groups "lua-l" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lua-l+un...@googlegroups.com.

sur-behoffski

unread,
Apr 8, 2025, 9:03:04 PMApr 8
to lu...@googlegroups.com
On 2025-04-09 05:45, Warner Losh wrote:
> [...]

It turns out that the continue patch does exactly what the
"goto ::continue::" code suggested here does -- adds a new
keyword, "continue". When used, inserts (effectively) a
"goto ::end_of_innermost_loop::" instruction, and also adds
a suitable label at the end of the innermost loop construct
in the VM code.

----------------

On "goto considered harmful", my understanding is that it
was written at a point in time when mature flow control
statements (for, do...while, while * do...done, and
if...then...elseif...else...end) were not part of the language
proper, and so goto was used to emulate them.

I still believe disciplined goto has a place in modern code:
e.g.

```
{
Status = 0; /* Assume no error. */
if (! auth_check_1) {
Status = ENOAUTH1;
goto failed_at_auth1;
}

/* Obtain temporary resource for use. */
Obj->mem = malloc(1024);
if (! Obj->mem) then
Status = ENOMEM;
goto failed_at_resource1;
}

if ( auth_check_2(Obj)) { /* This call requires Obj->mem */
Status = ENOAUTH2;
goto failed_at_auth2;
}

/* Remainder of processing within function body... */

/* Completed processing. */

/* FALLTHROUGH */

/* Tidy up at end of function. In particular, release
all temporary resources acquired during operation. */

/* ... (More cleanup points here.) ... */
failed_at_auth_2:
/* Discard temporary memory resource. */
free(Obj->mem);

/* FALLTHROUGH */

failed_at_resource1:

/* FALLTHROUGH */

failed_at_auth1:

/* FALLTHROUGH */

return Status;

}

```

[I acknowledge that free(NULL) is valid, and the code above could
be written more linearly to exploit that. "resource1" is a hint
that some more-complex case could appear there, and the code is
mainly for demonstration purposes.]

--

cheers, s-b etc.

Martin Eden

unread,
Apr 8, 2025, 9:35:45 PMApr 8
to lu...@googlegroups.com
On 2025-04-08 21:20, Frank Dana (FeRD) wrote:
> Some would argue goto is harmful, but it has its place in clean code.

"Considered harmful" lore:

Edgar Dijkstra submitted article "A Case against the GO TO Statement."
to ACM in 1968. Editor Nicklaus Wirth renamed it to
"Go-to statement considered harmful". And thus created new direction of
holy wars!

[paper]: https://www.cs.utexas.edu/~EWD/ewd02xx/EWD215.PDF
[published article]:
https://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.pdf
[edgar's reminiscence]: https://www.cs.utexas.edu/~EWD/ewd13xx/EWD1308.PDF

-- Martin

Martin Eden

unread,
Apr 8, 2025, 9:42:55 PMApr 8
to lu...@googlegroups.com
On 2025-04-09 03:35, 'Martin Eden' via lua-l wrote:
> Edgar

Edsger

> Nicklaus
Niklaus

Aaron B.

unread,
Apr 8, 2025, 10:40:00 PMApr 8
to lu...@googlegroups.com
On Tue, 8 Apr 2025 12:20:26 -0700 (PDT)
"Frank Dana (FeRD)" <fer...@gmail.com> wrote:

> On Tuesday, April 8, 2025 at 1:34:20 AM UTC-4 Mitchell wrote:
>
> Hi,
>
> > On Apr 7, 2025, at 10:39 PM, sur-behoffski wrote:
> >
> > <snip>
> >
> > (The one C idiom I'd love to see in Lua is the "continue"
> > statement,
>
> For this very reason I like to use “goto continue”:
>
> for k, v in pairs(t) do
> if not condition(k) then goto continue end
> -- do something with v
> ::continue::
> done


I find it cleaner (and more "Lua-like") to use a function. I use this
pattern often:


local function is_valid( v )
if v.value == 'correct' then
return true
end
return false
end

for k, v in pairs(t) do
if is_valid(v) then
do_stuff()
end
end


It's more verbose but also very readble; and easy to modify or extend
the validity logic later.

Also if the logic of what needs to get skipped is complicated and needs
many 'if' statements, this method is much more readble than trying
the same thing with a goto.

--
Aaron B. <aa...@zadzmo.org>

Daniel Krüger

unread,
Apr 9, 2025, 4:50:28 AMApr 9
to lua-l
Also everybody please bear in mind what the cost of new features is.

Roberto made a very good talk about it:
https://www.youtube.com/watch?v=gXdS3IftP0Y&t=1s

I think this is one key philosophy of the inventors of Lua; to not impose too many new features too quickly which could later restrict us.

I think one of the key advantages of Lua is the lack of change, I really like that it's unlike other languages/technologies where if you step away for several months or years and suddenly key aspects have changed and your old way of doing things doesn't work anymore.
As a bad example I would give the Javascript frontend frameworks (Angular, React, Svelte); every 12 months they make insane backwards incompatible changes, which breaks your whole frontend code or your project doesn't build anymore.

If there are new features proposed there should be an insanely strong reason for them to be added, because every new feature has a cost.

Roberto Ierusalimschy

unread,
Apr 9, 2025, 12:43:13 PMApr 9
to lu...@googlegroups.com
> Roberto made a very good talk about it:
> https://www.youtube.com/watch?v=gXdS3IftP0Y&t=1s

Thanks :-) If any one is interested, the slides of that talk are
available here:

https://www.lua.org/wshop17/Ierusalimschy.pdf

-- Roberto

Francisco Olarte

unread,
Apr 9, 2025, 12:52:05 PMApr 9
to lu...@googlegroups.com
On Wed, 9 Apr 2025 at 10:50, Daniel Krüger <daniel....@libelle.com> wrote:
> Also everybody please bear in mind what the cost of new features is.
...
> I think this is one key philosophy of the inventors of Lua; to not impose too many new features too quickly which could later restrict us.

I agree with that but:

> I think one of the key advantages of Lua is the lack of change, I really like that it's unlike other languages/technologies where if you step away for several months or years and suddenly key aspects have changed and your old way of doing things doesn't work anymore.
> As a bad example I would give the Javascript frontend frameworks (Angular, React, Svelte); every 12 months they make insane backwards incompatible changes, which breaks your whole frontend code or your project doesn't build anymore.

But I would not take lua as the paradigm of backwards incompatible
change AMONGST PROGRAMMING LANGUAGES, I think you are comparing apples
to oranges there by using FRAMEWORKS in your second sentence.

I routinely use C/C++/Perl5( 5 and 6 are really different languages
)/lua and if I'm not too confused the first three are better at
backwards compatibility. I do not have big problems with lua because I
cheat by allways bundling the interpreter source in the project, and
forward porting problems have not been too bad mainly because all my
lua code is in use and I always schedule rewrites after a new version,
but I wouldn't consider it the best AS A LANGUAGE.

Francisco Olarte.

Gé Weijers

unread,
Apr 10, 2025, 1:18:55 AMApr 10
to lu...@googlegroups.com
On Sun, Apr 6, 2025 at 11:20 PM bil til <bilt...@gmail.com> wrote:
 

local counter variables in numerical for loops should please behave
like local variables in (all?) other languages:

It should be no problem to modify the counter variable during the for loop.

So the warning "You should not change the value of the control
variable during the loop" in RefMan 3.3.5 should be "stripped".


This code works correctly, and prints out the 4 largest integers in your Lua implementation:

local m = math.maxinteger

for i = m-3, m do
    print(i)
end

Coding the same program in C is not quite trivial. This does not work because i <=m is always true:

long m = LONG_MAX;
for (long i = m-3; i <= m; i++)
  printf("%ld\n", i);

The Lua VM FORPREP and FORLOOP instructions do a bit more work to make edge cases work correctly, and who knows how that will interfere with modifying the loop counter.

What do you propose this does:

for i = 1, 10 do
  i = i + 0.5
  print(i)
end

I would suggest changing the Lua compiler code to implicitly mark loop variables as <const> instead, and just block messing with loop internals.

--

Roberto Ierusalimschy

unread,
Apr 10, 2025, 9:20:44 AMApr 10
to lu...@googlegroups.com
> I would suggest changing the Lua compiler code to implicitly mark
> loop variables as <const> instead, and just block messing with loop
> internals.

This has already been done for the next version, 5.5:

https://github.com/lua/lua/commit/b2f7b3b79f3117885b265575f6c5dbf934757797

-- Roberto

bil til

unread,
Apr 10, 2025, 9:54:54 AMApr 10
to lu...@googlegroups.com
Am Do., 10. Apr. 2025 um 15:20 Uhr schrieb Roberto Ierusalimschy
<rob...@inf.puc-rio.br>:
>
> I would suggest changing the Lua compiler code to implicitly mark
> loop variables as <const> instead, and just block messing with loop
> internals.

This has already been done for the next version, 5.5

... thanks, this sounds good for me.. .

Yao Zi

unread,
Apr 10, 2025, 12:11:33 PMApr 10
to lu...@googlegroups.com
Cool stuff!

Though there're some features I'd like to see, I'm actually pretty fine
with our current Lua; they're icing on the cake instead of essentials.

Thank you for maintaing such a wonderful language!

> -- Roberto
>
> --
> You received this message because you are subscribed to the Google Groups "lua-l" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to lua-l+un...@googlegroups.com.
> To view this discussion visit https://groups.google.com/d/msgid/lua-l/20250410132035.GA714014%40arraial.inf.puc-rio.br.
Reply all
Reply to author
Forward
0 new messages