Readability question.

111 views
Skip to first unread message

Francisco Olarte

unread,
Jun 5, 2026, 2:38:38 AM (8 days ago) Jun 5
to lu...@googlegroups.com
I've found myself writting the expresion

(a and b and combine(a,b)) or a or b

is what it does "obvious"? ( I would not declare it idiomatic, as "a
and b or c" )

( context: a,b are tipically optional numbers, or tables, combine is a
function which "merges" tables, or something like add/mul/gcd,
expresion is used typically in return, local initialization or
parameter calculation to a function ( which is lot like local
initialization ). I fear maintenance problems ahead if meaning not
obvious.

Francisco Olarte.

Sainan

unread,
Jun 5, 2026, 7:08:29 AM (7 days ago) Jun 5
to lu...@googlegroups.com
If you have to ask, it's probably not readable. I would make the conditional an actual conditional:

if a and b then
x = combine(a, b)
else
x = a or b
end

Now, one might also be able to see more obviously that if a=nil and b=nil then x=nil, so one could write `x = a or b or error()` in the else clause if that's undesired.

-- Sainan

Francisco Olarte

unread,
Jun 5, 2026, 7:38:15 AM (7 days ago) Jun 5
to lu...@googlegroups.com
Sainan.

On Fri, 5 Jun 2026 at 13:08, 'Sainan' via lua-l <lu...@googlegroups.com> wrote:
>
> If you have to ask, it's probably not readable. I would make the conditional an actual conditional:

I ask because it is readable for me, but not sure how it is for others.

> if a and b then
> x = combine(a, b)
> else
> x = a or b
> end

This is the actual code I have been using, but was tempted to play a
little "lua-golf".

> Now, one might also be able to see more obviously that if a=nil and b=nil then x=nil, so one could write `x = a or b or error()` in the else clause if that's undesired.

Yep, but error avoiding is specifically desired. One specific scenario
in which I use the construct is cascading configuration merging, where
combine being something like a table version of "a+b". For all this
kind of things I normally have an unchecked version ( combine in the
example ) and a checked one ( i.e., replacing x= with return on your
example, I have even been tempted to do

function guard(f) return function(a,b) return (a and b and f(a,b)) or
a or b end end

in which case it would be less problematic ( buried in a lib,
explaining comment, no worries about maintenance, can even have the
equivalent if commented ) ) .

But, after looking at some bytecode, I think the one liner may be
useful on some REPL experiments, but it is not really equivalent if
combine(a,b) returns something false-ish or has multi-returns ( and,
on the returning cases, inhibits tail calls and forces single return
).
Falseish is not normally used by me, but clipping multi-returns is, as
I may want come combine() to return fail, "combine error...".

Thanks for the feedback.

Francisco Olarte.

Vadim

unread,
Jun 5, 2026, 7:50:32 AM (7 days ago) Jun 5
to lu...@googlegroups.com
>> If you have to ask, it's probably not readable. I would make the conditional an actual conditional:
>
>I ask because it is readable for me, but not sure how it is for others.

This was the answer already :) I agree. To paraphrase the above: when in doubt, don't do it this way.

I might have had the same temptation more than once. When you are used to this idiomatic construct, it's not a puzzle. Yet it ought to increase the cognitive load. Suddenly you have a lot going on in one line. It acts like a mental anchor, imho. Lines with variable  assignment usually aren't doing logic and much side-effects.

I would _generally_ prefer the visual structure of the obvious if-then-else when dealing with actual conditional expressions. Unless... you need to do a bunch of these calls throughout your code* and it remains readable despite real var names.

* why not make it another function if this pattern is common?
Cheers,
Vadim

Scott Morgan

unread,
Jun 5, 2026, 8:05:06 AM (7 days ago) Jun 5
to lu...@googlegroups.com
On 05/06/2026 07:37, Francisco Olarte wrote:
> I've found myself writting the expresion
>
> (a and b and combine(a,b)) or a or b

I think the combine call is a bit of blocker. Not knowing exactly what
it does, even if it's clear with a bit of though (and checking the code).

But...

(a and b and (a + b)) or a or b

Feels a bit too much as well. Just a step too far for quick
comprehension. You can understand it, but it takes more time than ideal.

Scott

Francisco Olarte

unread,
Jun 5, 2026, 10:12:36 AM (7 days ago) Jun 5
to lu...@googlegroups.com
Hello Scott...

On Fri, 5 Jun 2026 at 14:05, 'Scott Morgan' via lua-l
<lu...@googlegroups.com> wrote:
> On 05/06/2026 07:37, Francisco Olarte wrote:
> > (a and b and combine(a,b)) or a or b
> I think the combine call is a bit of blocker. Not knowing exactly what
> it does, even if it's clear with a bit of though (and checking the code).

Well , I said: "combine is a function which "merges" tables, or
something like add/mul/gcd,", looked clear to me.

Anyway, after a little thought and experimentation, and some look at
bytecodes ( mainly 4 fun, it's not going to matter a lot ), I have
opted for a multi if approach:

local function guard(f, a, b)
return function(a,b)
if a == nil then
return b
end
if b == nil then
return a
end
return f(a,b)
end
end

With more explicit nilness testing, and a return preserving tail-call.

Francisco Olarte.

Martin Eden

unread,
Jun 5, 2026, 1:06:49 PM (7 days ago) Jun 5
to lu...@googlegroups.com
On 2026-06-05 16:11, Francisco Olarte wrote:
> local function guard(f, a, b)
> return function(a,b)
> if a == nil then
> return b
> end
> if b == nil then
> return a
> end
> return f(a,b)
> end
> end


Hello Francisco,

It still bad for me:

  if a == nil then
     return b
  end
  if b == nil then
     return a
  end

We're testing for one thing and returning other.

I think I would settle at

local function safe_calc(f, a, b)
  return
    function(a, b)
      if not (a and b) then return (a or b) end

      return f(a, b)
    end
end

-- Martin

Francisco Olarte

unread,
Jun 5, 2026, 2:16:27 PM (7 days ago) Jun 5
to lu...@googlegroups.com
Hello Martin.

On Fri, 5 Jun 2026 at 19:06, 'Martin Eden' via lua-l
<lu...@googlegroups.com> wrote:
> On 2026-06-05 16:11, Francisco Olarte wrote:
> It still bad for me:

"Para gustos los colores", we say around here. But I'll try to spain.

> if a == nil then
> return b
> end
> if b == nil then
> return a
> end
> We're testing for one thing and returning other.

Which is typical when testing for "badness", I read this as "if a is
bad, use b, if b is bad, use a, if both are good, use both". It lacks
an "if both are bad" at the start, which I could use too, but it is
not relevant in this case. In fact every usage of 'return a and b',
'return a and b or c', even 'return a or b' depending on how yoou look
at it, tests one thing and return ( or uses if you are doing a
parameter pass or assignment ) other.

For the full version I would use
if (a==nil) then
if (b==nil) then
return --nothing.
else
return b
end
else -- elseif would avoid a nesting level, but spoil symmetry, as
would collapsing elses above.
if (b==nil) then
return a
else
return f(a,b) --both
end
end

Anyway, the way I code it reflects a bit my assembly origins, and my
distate for too much nesting. For a C-style block delimiters I would
probably code the first two ifs as one-liner, but with then-end style
I nearly never use it. Now that I see it I may switch to
if a==nil then if b==nil then return else return b end end

I tested some, and liked how the byte code reflected the source style,
this is why I used that. But I have to stop playing with byte code or
I will write byte-assembler just to scratch the itch caused by the
unsuppressed redundant function end returns.

> I think I would settle at
>
> local function safe_calc(f, a, b)
> return
> function(a, b)
> if not (a and b) then return (a or b) end
> return f(a, b)
> end
> end

Yep, classical else avoidance, one liner for simple guard conditions.
I do it that a lot, for speed sometimes ( I have a hard time
remembering I'm not on a 8080 anymore ), but, lacking an optimizer,
this evaluates too much. Granted, lua seems to not have an opcode for
==nil and makes a constant and to hold it, but I like how my version
compiles, and the nearly minimal set of ops. But they are all the same
at the end.

Francisco Olarte.

Sean Conner

unread,
Jun 5, 2026, 4:11:57 PM (7 days ago) Jun 5
to lu...@googlegroups.com
It was thus said that the Great Francisco Olarte once stated:
> Hello Martin.
>
> On Fri, 5 Jun 2026 at 19:06, 'Martin Eden' via lua-l
> <lu...@googlegroups.com> wrote:
> > On 2026-06-05 16:11, Francisco Olarte wrote:
> > It still bad for me:
>
> "Para gustos los colores", we say around here. But I'll try to spain.
>
> > if a == nil then
> > return b
> > end
> > if b == nil then
> > return a
> > end
> > We're testing for one thing and returning other.
>
> Which is typical when testing for "badness", I read this as "if a is
> bad, use b, if b is bad, use a, if both are good, use both". It lacks
> an "if both are bad" at the start, which I could use too, but it is
> not relevant in this case. In fact every usage of 'return a and b',
> 'return a and b or c', even 'return a or b' depending on how yoou look
> at it, tests one thing and return ( or uses if you are doing a
> parameter pass or assignment ) other.

I would write this as:

if a then
if b then
return combine(a,b) -- a b
else
return a -- a /b
end
elseif b then
return b -- a /b
else
return -- /a /b
end

I personally don't like comparing directly against nil unless 'false' is a
valid thing to return (for instance, a = false b = nil). But that aside,
this reads better to me as if we have an a and not a b, we return a. I've
marked the four cases this covers (where /a means "not a").

-spc

Francisco Olarte

unread,
Jun 7, 2026, 7:23:29 AM (5 days ago) Jun 7
to lu...@googlegroups.com
Sean:
On Fri, 5 Jun 2026 at 22:11, Sean Conner <se...@conman.org> wrote:
...
> I would write this as:
>
> if a then
> if b then
> return combine(a,b) -- a b
> else
> return a -- a /b
> end
> elseif b then
> return b -- a /b
> else
> return -- /a /b
> end
>
> I personally don't like comparing directly against nil unless 'false' is a
> valid thing to return (for instance, a = false b = nil).

Yep, I did not clarify it, but when thinking on packing it in a
"guard" func this was specifically the case, as I have lots of tables
where I must distinguish false from absent. Were not this the case I
probably would have gone your way, positive testing first, which I use
normally except when trying to avoid deep nesting, typically in error
cases.
if error_condition_ then

> But that aside,
> this reads better to me as if we have an a and not a b, we return a. I've
> marked the four cases this covers (where /a means "not a").

For just-falseness, this is just the case. My version, nilness of
falseness versions, can have the if swapped to do it this ways.
The reason I tested for invalid a/ return b first, unexplained again,
if that for my usual combinator cases I many times start with nil
initial values, so I went for what I thought faster. Anyway, if we
have not a, b I think my version skips first return, returns a in
second. As I tested nils, returns will be equal. And yes, for the full
library version I would go for your solutions or some of the variants,
I think I wrote some too, with double nested if-then-else, as I need 4
returns ( In fact your version, switching conditions to ~= nil ( which
I dislike, for being a negative test and someone changing the default
keyboard handling for ES to ~ being a modifier which leads to
uncountable ≃ being put by me in the source code ( maybe useful for
other language, but in spain we only use it for ñÑ and we have a
dedicated key for it ) ) lends itself well to make use of the implicit
return at function end, which to me is pleasing ( nothing to combine,
do nothing and fall of, but just is weird and personal ).

Francisco Olarte.
Reply all
Reply to author
Forward
0 new messages