Other practical example why ternary is bad: Many code-coverage solutions break on ternary because they don't correctly see that one of the branches was missed in tests.
if c
x=1
else
x=2
If I ever want to change x, or refactor this code some other way, its a more brittle process over x=c?1:2The ternary expression also takes up much less space so there is less of an emphasis on it, this can be a stylistic tool in a programmer's toolbox
Of course there are differences between LuaJIT and Luau that I think influence their decisions on possible ternary expression features:
- Luau users are disproportionately beginners to programming that I believe would find the if-then-else expression syntax easier to learn; LuaJIT developers have a larger user base of professional devs wanting to make their code faster, and they will probably be more familiar with the `x ? y : z` style since it's used in plenty of other languages.
- Luau is a lot faster moving in its development than LuaJIT in terms of language features, the Luau team just wanted to move people to ternaries from `x and y or z` because it's easier to optimise in a normal interpreter; LuaJIT, with their JIT, I assume would be able to more easily implement optimisations for constructs like `x and y or z` despite its slight semantic differences (my assumption on why the change is being considered now rather than earlier).
Does that operator compile to faster assembly that if I make the same logic with verbose `if` logic? Is that a language specific outcome?
cond1 ? res1 :
cond2 ? res2 :
cond3 ? res3 :
or_else_res
If they are truly nested, then that is confusing. But if you have an if-else chain, then it can be quite readable. (cond (cond1 res1)
(cond2 res2)
(cond3 res3)
(t else_res))
=) if cond1 then res1
else if cond2 then res2
else if cond3 then res3
else or_else_res
or if cond1 then res1
elif cond2 then res2
elif cond3 then res3
else or_else_res
what is most lua-like?The ? is basically an attempt to use fewer if/else, at the cost of condensed if-else like structure. I always need to look at both parts after the ? whereas in a single if or elsif I don't. case/when in ruby is even better here e. g. regex check:
def foo(i)
case i
when /^cat/
handle_cats
when /^dog/
handle_dogs
(I ommitted the "end"s here to just focus on the conditional logic.)A lot of these changes make sense (although some of them are a bit too TIMTOWTDI for my taste) - but perhaps LuaJIT 3 would benefit from a change of name as well? Certainly with all these changes, it would be more like a separate language than merely a JIT-compiled version of Lua.
What on earth is this supposed to mean?
That takes me back a bit. It's a perl-ism. I used to think it was a great design feature but I've come to strongly prefer "There should be one way to do it, and it should be obvious"
> Certainly with all these changes, it would be more like a separate language than merely a JIT-compiled version of Lua.
I agree. I suggested this on the GitHub issue but got nothing but downvotes.
https://www.lua.org/versions.html#5.3
https://www.lua.org/manual/5.3/manual.html#3.4.2
Looks like LuaJIT is catching up, but calling these "syntax extensions" is confusing. Is the intent to hold LuaJIT fixed against some earlier Lua version (I guess 5.1) and adopt newer syntax piecemeal?
I welcome the compound assignment operators. Playdate's version of Lua also has that extension.
Yeah. PUC-Rio went in a direction that Mike Pall didn't want to follow. Something to do with garbage collector finalizers, if I remember correctly, which is a notoriously thorny issue in every language it exists.
The removal of scanning for changed userdata finalizer meta method in 5.2 is just a commonsense fix for bad design that made GC atomic phase run time, thats not incremental scale up with the number of GC userdata objects alive no matter if they have a finalizer or not.
Why though? What does changing `and` to `&&` actually achieve? Were people confused?
Changing the syntax seems very surface level. It's not actually fixing any problems, just making Lua no longer look like Lua. It's not going to help anyone write/learn Lua. It will make everything more complicated as there are now two ways to do everything.
This feels like adding braces to Python because you don't like indenting your code.
Now this I can get behind...
In general, I would expect symbolic operators to be desirable in complex boolean expressions, because "loud punctuation" stands out among English words when reading the code.
open my $fh, '<', 'input.txt' or die;What is a practical use case where the lower precedence makes sense?
1. statement if (condition || something)
2. (statement if condition) or something
Also consider AI, that has a greater training base of JavaScript than Lua. So making Lua look more like JS, should improve output and reduce mistakes.
My ultimate goal was to support LuaJIT in Rust as well but this does not make it easier.
For example, what’s the performance like?
In a mix of official and unofficial benchmarks wall clock performance is ~1.4x as fast as C Lua and the memory usage is ~1.7x.
So performance is worse to be clear but within range. There’s some performance improvements I haven’t gone for yet that would get it down to ~1.1 I think.
> My ultimate goal was to support LuaJIT in Rust as well but this does not make it easier.
I think you could stop right before the syntax extension.
If you have any Lua use cases let me know I’m looking for more real world use use cases to justify the effort here.
I think where it would be most helpful is converting a codebase and being able to easily run tests to ensure behavior is the same.
I created a github gist https://gist.github.com/ianm199/5ba0366376eca673142e1f0c79b4... that explains what is practical (I used AI for this to be clear feel free to skim).
Do you have a use case in mind? Would love to chat or take a look at an github issue if you create one.
Languages should probably protect themselves with trademarks or something.
Some of these really look like QoL improvements. I'm not convinced ternary statements are an ergonomic improvement in particular. The examples given don't make a compelling case, 'visually tidy' is not the same as readable.
There are real improvements though, such as ?. and ??= that help with default-nullable everything.
Ternary is very useful, but it I'd rather see it implemented idiomatically:
pos += (if forward then +1 else -1)
Structural pattern-matching could be fantastic, but no syntax is suggested.Now, the object systems do look similar, but that seems to be a case of convergent evolution: Javascript took direct inspiration from Self, whereas Lua's system is based on a more generic fallback mechanism for table access.
Zig and Rust have addressed the problem of how the result of a block expression should be presented, but neither solution seems particularly satisfying to me.
In Rust, blocks may end with an expression, giving them a non-void result. But a block may also end in a statement, the only difference being that the statement ends in a semicolon, in which case the expression still has the void result, and I think that semicolon being the only difference makes it hard to scan at a glance where values come from.
In Zig, blocks may give non-void results by `break`ing out of them with an expression. But break normally ignores blocks and break out of loops only, so to break out of blocks you have to provide a label for it and give that when you break so as to break out of the named block and not the outer loop, e.g. `const x = label: { break :label 35; }`. That creates a problem of one of the most difficult classes in software engineering: naming things. Ideally I think `break` from a block should have its own keyword, e.g. `const x = { give 35; }`
Right now, `if` in expression position is just a syntax error ("unexpected symbol")
But other than that, yeah, detecting "if" in the expression position is pretty unambiguous. No idea why most languages went with "cond-expr ? then-expr : else-expr" bracketed syntax instead.
But e.g. ml-family languages (like OCaml, F#, Haskell) and Rust just have the *if* expression that has a non-void value. If your language accepts expressions as statements (most do?), then I think that should just be compatible out of the box.
Oh, and Lua most famously does not accept expressions as statements. Which, now that I think of it, would actually evade most of the parsing complications.
local x = condition ? value_a : value b
local x = condition and value_a or value_b x = a ? b : c # x is b, same as you would if a {x=b} else {x=c}
lua and/or a,b,c = true, 1, 2
x = a and b or c -- x is b
a,b,c = true, false, 2
x = a and b or c -- x is c
The or is dependent on its previous operand, so b will return false and skips to c, even if you meant for it to be b. you must use an if then else. However, you can have more than a ternary, if there is no need for short-circuit evaluation, as in, any of the operand is not a function CALL like c(), and you want to remain inside an expression, then you can do this insteadselect (select is a native C function, this is faster than the table creation below)
x = select(a and 1 or 2, b, c)
table creation/selection x = ({false,2})[a and 1 or 2]
of course, doing something like local x; if a then x=b else x=c end
does not look so bad> E.g. true and false or 42 returns 42, whereas true ? false : 42 returns the (expected) false.
local x = y and y + 1 or 0
The knuckle heads are already using them everywhere.I'm proud of it and thankfull to the Lua/Luajit projects.
var ary = [1,2,3,4]; //Array style declaration, syntax sugar for {}
Is not a good idea. I tried using Haxe with Lua target at some point - the mismatch between what you think you get with [], and what you actually get in Lua, requires either a lot of boilerplate (Haxe compiles [] to Array objects), or a chronic WTF from all the new people reading your code (and from yourself, after a few weeks to months of disuse). If you want [1,2,3], make it behave more like an array - or just leave it out, would be my advice. Lua doesn't have arrays, and adding syntax that suggests it does will be a permanent footgun for your users, I think.So shouldn't it have a new name?
On the contrary, we can claim that luajit has stabilized lua for implementations and for users (strengthening Lua 5.1 dominance, which makes the experience more homogenous across apps).
Personally im a fan of introducing ternaranary operator in lua. Everyone uses `x and y or z` as a ternanary which i find way more confusing than ?:
obj?.:method(…)Edit: meaning he can come back anytime.
I don't want to spam it in repo, so leaving it here: he is kind of a hero doing this work and I (hopefully we) am very grateful for his contribution to this world.
In effort to not pollute the github issue, and hopes that the authors read this thread, I will put some of my thoughts here. There are 3 main strengths of Lua: Embeddable, Fast, and Small(easy to learn). I worry some of these changes divert from the last, expanding the language into a more complicated language.
Here is a list of things already implemented in PUC Lua so can be considered safe to add:
● ~ a Bitwise negate
● a & b Bitwise and
● a | b Bitwise or
● a ~ b Bitwise Xor
● a << b Left-shift
● a >> b Logical right-shift
● a // b Floor divide
● break Break statement
Don't get me wrong, I love some of these quality of life changes like: ● Const keyword: changing const from `local a <const> = 42` to `const a = 42` is far better syntax. The bracketed syntax was never a good idea.
● nil-Coalescing and safe navigation are great additions as they are basically macros at the parsing stage.
● Compound assignment is also basically a macro at the parsing stage as well. Lua should already have this honestly.
● Ternary Operator: I *like* it and it will help the stumbling block of the `a and b or c` common pattern already in use. Though I think (like others have stated) the If/then/else syntax would be more inline with the language, similar to ruby and would enable far more emergent behaviour. However it does establish a new pattern that the last value in a block is a return value similar to ruby so I am conflicted about that.
● `continue` it is nicer than a goto and is helpful.
● String interpolation: I honestly don't love lua's concat operator `..` so honestly string interpolation would be a nice to have and a feature of many modern languages. However I do worry about it's effect on parsing performance, and complexity of the language.
● Underscores in numbers: *shrug*
These are great ideas for the language but I would want all lua versions to support them, not just JIT. These are things that I think are a distraction: ● The `and` `&&` and `or` `||`. This just goes in the wrong direction for lua. It is often confusing in ruby (especially because of precedence issues) but also lua is a wordy language. It has `do` `end` blocks instead of brackets. It adds ambiguity for no reason.
● Short form function syntax. Lua does not need this and I am not sure anyone asked for this. Why `a = |x| do ... end` is more helpful than just `a = function(x) ... end` is unclear and would love to hear more about why this is being considered.
● Named varargs: It may be nice, but there is no real reason to add this. If you wanted a name for your varargs you could do `local name = ...` or just use the `args` variable already available in every function.
● Switch/Match/Select Statements: An optimized if/else block works just as well and another expansion of a small language.local gauge = count + (direction == "up" ? 10 : -10)
I imagine these changes make the original Lua adepts think their training wheels have come off. The language now looks like any other. That's a good thing to me, and it will help with the adoption of the JIT, but the whole language could have been syntax modernized as a result. But.. when the work is done someone else can fork it into something independent from its Lua roots.
From that perspective the conditional operator seems defensible, where it would be feature creep otherwise, as it is generally unloved elsewhere.
But which Lua?
Lua as implemented by LuaJIT is a fork of the language at this point. It's not fully compatible with PUC Lua (the reference implementation) and LuaJIT does not support features from the latest Lua version.
Likewise, going from `and` and `or` to `&&` and `||` would be a dispiriting regression. This is something that Zig got right.
The part I'd call a hassle is the different kinds of right shift but you have that same hassle if you use keywords.
I like using the and/or keywords for logical operations. Now let's make bitwise look significantly different from that.
This stuff (especially the ternary) are a step backwards. There is just no reason to waste | on a bitwise or that gets used at 1% of the frequency of the standard or. In the future you might have a better use for it (pipeline syntax, sum or union types come to mind in other languages).
I dislike basically everything about these syntax extensions.
Also a syntax for types can repurpose most symbols without being ambiguous.
And you can overload the bitwise operators. You can configure __bor to give you pipelining right now.
[1] https://en.cppreference.com/cpp/language/operator_alternativ...
Also, I love this kind of pragmatism:
> Exponentiation assignment a ^= b has been deliberately omitted to avoid a predictable pitfall: this is how xor assignment is written in most other computer languages. Also, a syntax for exponentiation assignment is rarely asked for.
A ‘defer’ for closing files or deleting temp files at the end of a script will make life more enjoyable.
Love2D does it as well: zip -9 -r SuperGame.love . cat love.exe SuperGame.love > SuperGame.exe
This doesn't work with ELF files, though.
Instead of obj?.:method?.(…) it would be like obj#:method#(…)
Replace # with your favorite extra character instead of questionmark.
So you'd have: obj?:method(…)
1) Ease of learning, ideally minimal deviant behaviour (eg i consider lua tables to be a new concept in itself)
2) Reasonably fast. Not as much as lua jit but even half would be good enough
3) Mature
4) Has Rust bindings
That's just one example of so many more. I get that lua occupies a useful niche with its focus on embedded systems, but lua is not really a well-designed language in general. JavaScript has a similar problem.
if x + y + z > a
or verylongconditionalhere ()
or anotherverylongconditionalhere ()
then
...
after `if` and `elseif` the parser simply goes on until it finds `then`. for long,list,of,variables,here
in ageneratorhere(bigparameterhere)
do
end
and local x do
-- everything after is just here to define x
end
I'm still a little irked it works so well, the only alternative would be for the language to have labeled blocks. but that might be too terseIn Ruby you can choose between "then" and a newline.
This is very pot calling the kettle black.