It was thus said that the Great Shahar Band once stated:
>
> I’ve put together a *Lua style guide* to help improve consistency and
> readability when writing Lua code. It’s inspired by common patterns I’ve
> seen in the community, with some additional conventions that I’ve found
> useful in practice.
>
> I’d love your thoughts—suggestions, criticisms, or additions are all
> welcome! If you find it useful, please feel free to contribute via pull
> requests or issues.
Since you asked ... (disclaimer: I'm not fond of style guides myself
because they tend not to hew towards my opinions).
First comment: you link to another Lua style guide at the end. Why not
just use that guide? Why bother with a new guide at all? Continuing
onwards, from section 1.1:
> * Use snake_case for local variable names.
Why not lowerUpper or UpperUpper for local variables? Or even
runonsentance? It might be a bit of an ask to expect rationals for these
decisions, but it would be nice. If you were to ask me for rationals for
how I format my code, I can a rational. You may not agree with it, but I do
have reasons.
> * Use UPPER_CASE_WITH_UNDERSCORES for constants.
This comment is in conjuction with section 1.3 (constants) and 3.1
(function names). Technically, most functions, once defined, don't change
and are therefore a constant. I know you are really talking about magic
numbers, but other values can be constants too---in my CBOR implementation
[1] if you need to use CBOR "null" and "undefined" they can be implemented
as:
null = {}
undefined = {}
in order to get unique values that can be checked. Technically, per your
guide, these should be NULL and UNDEFINED but I was matching CBOR terms, so
lowercase they are.
And because values other than numbers can technically be a constant,
shouldn't the following also be true if I use your style?
local LPEG = require "lpeg"
local function FOO(a,b,c) ... end
Loaded modules are, for the most part, treated as constants in most Lua
code I've seen, and it's rare (although not uncommong) to never redefine
most functions to technically, they are constants as well.
And does this apply to constants within a module? The LPeg module
contatins a .version member, a string, which is the version. Shouldn't it
technically be .VERSION?
Sorry, just taking this advice to it's absurd end here.
You talk a bit about strings in section 1.4. I treat '' and "" as
semantic elements. Strings that the user can see get "" and strings used
more for enumeration or non-visible reasons get ''. Some examples:
-- here, 'error' is an enumeration of the level, while
the other string is user visible
syslog('error',"could not read %q: %s",filename,errno[err])
-- here, the filename is in quotes (even though it's not user
-- visible, it *is* a name) and the mode is in '' because it's
-- more of an enumeration of modes than a string
file = io.open("datafile.txt",'r')
If the string will contains either double quotes or single quotes, I'll
use the bracket form to avoid excessive esacping (mostly when generating
HTML output).
> * Escape only when needed (e.g., \n, \t, \\).
Why even mention this?
In section 1.5, nil and NaN can't be used for indexing (it's not just
nil). It takes some work to get a NaN (0/0 is one way). Also, I'm very
surprised you didn't mandate brace placement in tables (points off for using
K&R style braces---boo! Not a fan!)
Why does section 2 even exist? Is this a style guide, or the fundamentals
of Lua coding? Also, for looping, you forgot recursion:
function ItemsInTable(t,c)
c = c or 0
if not t[c] then
return c
else
return ItemsInTable(t,c+1)
end
end
I'm not saying that's a performance example of calculating the length of a
table, but that it *is* an example of a loop in a recursive style.
No further comments about section 3 that haven't been said already.
In section 4.1, you might get some strong pushback to using tabs in source
code, if just from the Python people (where white space is used
semantically). They're hard to distinguish from a space charater, and they
seriously mess up diffs when viewing patches.
In section 6.2 your example for using assert() is not a very good one.
You state:
> Use assert for programmer errors and preconditions.
but the example is neither an error or a precondition. As I don't really
use assert() in Lua, the only good example of a precondition I can come up
with is an example from C (from my Lua CBOR implementation):
static void cbor_cL_pushvalueN(
lua_State *L,
int typeinfo,
unsigned long long int value,
size_t len
)
{
buffer__u result;
size_t idx = sizeof(buffer__u);
assert(L != NULL);
assert(
((len == 1) && ((typeinfo & 0x1F) == 24))
|| ((len == 2) && ((typeinfo & 0x1F) == 25))
|| ((len == 4) && ((typeinfo & 0x1F) == 26))
|| ((len == 8) && ((typeinfo & 0x1F) == 27))
);
for (uint8_t b = (uint8_t)value ; len > 0 ; b = (uint8_t)(value >>= 8) , len--)
result.b[--idx] = b;
result.b[--idx] = typeinfo;
lua_pushlstring(L,&result.c[idx],sizeof(buffer__u) - idx);
}
and an example of checking for a programming error comes from some other C
code I have:
assert((size_t)(buff - obuff) < size);
This to ensure that the loop that preceeds this statement didn't overrun
the buffer. Your example is checking the result of io.open(). To me,
that's an incorrect usage for assert(). But as I said, I don't use it in
Lua (preferring for the system to detect errors) but in C (where the system
generally can't).
In section 9, you state:
> Keep metatable usage minimal and explicit.
What do you mean by this? The use of metatables is anything but explicit,
as it's used to overload common operations. And I don't get the point of
your example. First, because a point doesn't *have* a length, so defining
such a function for a Point object is meaningless. Second, you seem to want
people to avoid metatables entirely given you bad example of a bad example.
The rest of the guide seems to be fairly generic advice. Not much to say
about it.
-spc
[1]
https://github.com/spc476/CBOR