hisham hm

🔗 When listing repeated things, make pyramids

Often, in code, we have to write lists of repeated things. For example, attribute initialization in Java constructors:

this.foo = foo;

or required modules in Lua:

local foo = require("foo")

There are a few different ways people stack these when they need to list a number of them: randomly, alphabetic, aligned… working on a codebase that has all these approaches in different modules, I realized that “pyramid” is best. Let’s compare a few examples:

Random

This is what you end up doing if you don’t really think about it:

this.medium = medium;
this.aLongOne = aLongOne;
this.foo = foo;
this.veryLongOne = veryLongOne;
this.short = short;

⊖ ⊖ very bad to read - your eyes move back and forth horizontally and need to scan the whole thing vertically
easy to maintain - just add or remove entries arbitrarily

Alphabetical

This is what you end up doing if you get annoyed about the order when writing. I did this for a while.

this.aLongOne = aLongOne;
this.foo = foo;
this.medium = medium;
this.short = short;
this.veryLongOne = veryLongOne;

bad to read - your eyes move back and forth horizontally, but it’s easy to scan vertically
easy to maintain - no question where a new entry should go

Aligned

This is what you end up doing if you start to get annoyed when reading. Readability is more important than writability, after all!

this.aLongOne    = aLongOne;
this.foo         = foo;
this.medium      = medium;
this.short       = short;
this.veryLongOne = veryLongOne;

⊕ ⊕ very easy to read easy on the eyes horizontally, and if alphabetical it’s easy vertically as well
⊖ ⊖ very bad to maintain terrible for diffs, changes mess up `git blame` for unrelated lines

Pyramid

Finally, we get to the pyramid, which seems an ideal compromise keeping the advantages of an aligned list while avoiding its drawbacks:

this.veryLongOne = veryLongOne;
this.aLongOne = aLongOne;
this.medium = medium;
this.short = short;
this.foo = foo;

easy to read - easy on the eyes horizontally as the eyes follow the diagonal, and easy vertically as well as you usually know if you’re looking for a long or short word
easy to maintain - no question where entries go; you can use alphabetical order as a tie breaker for entries of same length

You can of course do the pyramid “ascending” or “descending”. I don’t really have a preference (and I couldn’t find any practical advantages to either yet).

In conclusion, it’s a silly little thing, but something that improves the ergonomics of the code and which I’ll try to adopt in my code more consistently from now on.

(PS: Of course, all of this applies to lists where the entries are not semantically related: when listing color components one would always do “red, green, blue”, and not “green, blue, red” :) )

🔗 Writing release announcement emails

Mailing lists are not exactly fashionable nowadays, but some of them remain relevant for some communities. The Lua community is one such example. As of 2017, a lot of what goes on in the Lua module development world still resonates in lua-l. With over 2500 subscribers, it’s a good way to kickstart interest in your new project.

Mailing list users tend to be somewhat pedantic about etiquette guidelines for posting, especially for announcements and the like. So, I usually follow this little formula for writing release announcement emails, which has been effective for me:

An example of an upgrade announcement is here:

[ANN] LuaRocks 2.4.2

Hello, list!

I'm happy to announce LuaRocks 2.4.2. LuaRocks is the Lua package
manager. (For more information, please visit http://luarocks.org )

http://luarocks.org/releases/luarocks-2.4.2.tar.gz
http://luarocks.org/releases/luarocks-2.4.2-win32.zip

Those of you on Unix who are running LuaRocks as a rock (i.e. those
who previously installed using `make bootstrap`) can install it using:

   luarocks install luarocks

What's new since 2.4.1:

* Fixed conflict resolution on deploy/delete
* Improved dependency check messages
* Performance improvements when removing packages
* Support user-defined `platforms` array in config file
* Improvements in Lua interpreter version detection in Unix configure script
* Relaxed Lua version detection to improve support for alternative
implementations (e.g. Ravi)
* Plus assorted bugfixes and improvements

This release contains commits by Peter Melnichenko, Robert Karasek and myself.

As always, all kinds of feedback is greatly appreciated.

Thank you, enjoy!

-- Hisham

An example of a new project announcement is here:

[ANN] safer - Paranoid Lua programming

Hi,

Announcing yet another "strict-mode" style module: "safer".

* http://github.com/hishamhm/safer

Install with
   luarocks install safer

# Safer - Paranoid Lua programming

Taking defensive programming to the next level. Use this module
to avoid unexpected globals creeping up in your code, and stopping
sub-modules from fiddling with fields of tables as you pass them
around.

## API

#### `safer.globals([exception_globals], [exception_nils])`

No new globals after this point.

`exception_globals` is an optional set (keys are strings, values are
`true`) specifying names to be exceptionally accepted as new globals.
Use this in case you have to declare a legacy module that declares a
global, for example. A few legacy modules are already handled by
default.

`exception_nils` is an optional set (keys are strings, values are
`true`) specifying names
to be exceptionally accepted to be accessed as nonexisting globals.
Use this in case code does feature-testing based on checking the
presence of globals. A few common feature-test nils such as `jit` and
`unpack` are already handled by default.

#### `t = safer.table(t)`

Block creation of new fields in this table.

#### `t = safer.readonly(t)`

Make table read-only: block creation of new fields in this table
and setting new values to existing fields.

Note that both `safer.table` and `safer.readonly` are implemented
creating a proxy table, so:

* Equality tests will fail: `safer.readonly(t) ~= t`
* If anyone still has a reference to this table prior
  to creating the safer version, they can still mess
  with the unsafe table and affect the safe one.

About
-----

Licensed under the terms of the MIT License, the same as Lua.

During its genesis, this module was called "safe", but I renamed it
to "safer" to remind us that we are never fully safe. ;)

-- Hisham
http://hisham.hm/ - @hisham_hm

Hope this helps!

🔗 The danger of simple examples

When discussing language syntax, people often resort to small examples using simple variables like foo or x, almost like “meta-syntactic variables”, i.e., to make clear these tokens are outside of the syntax under discussion.

One dangerous side-effect, though, is that these variables are always short and sweet. And syntax that works well with short variables doesn’t always work as well in real-world situations where they have to deal with the rest of the language.

Case the first

Recently we were discussing multiple assignment style in the Lua mailing list. Someone suggested this:

local a, b, c, d =
      e, f, g, h

…which makes the assignments “more parallel” than a single line and avoid writing lots of locals.
I think this a case where the over-simplified example is misleading.
With real-world looking variables, it would look more like

local cfg,              constraints, module_name,          initial_path =
      "default_config", {},          get_module_name(ctx), "/etc/myapp/default.config"

So yeah, It looks pretty with a, b, c but in the real world with significant names, this becomes a pain to maintain, and when we stuff too much in a single line, diffs are harder to read.

Case the second

Things always look good in tiny examples with single-letter variables. Which brings me to a gripe I have with an often-suggested Lua idiom: the famous t[#t+1] = v to append to arrays.

The reason why I think it’s so disengenious to defend t[#t+1] = v as the preferred idiom for appending to an array is because it looks good with a single-letter variable and five-line tutorial examples, but in the real world we use nested tables. In the end, table.insert(my.nested[data], v) is both more readable and avoids repetition:

Note how it’s not even necessarily shorter: in this realistic example the variable name dominates the size of the statement:

table.insert(my.nested[data], val)
my.nested[data][#my.nested[data] + 1] = val

Do I think table.insert is too long? Yes I do, I wouldn’t mind having a shorter idiom (many were proposed in the Lua list over the years, most of them were fine, but I’m not getting into them because we risk delving into syntactic bikeshedding again, so let’s avoid that).

Do I think it’s worth it to add local tinsert = table.insert to every program? No, I think this is worse than the t[#t+1] = v idiom, because I hate having to guess which abbreviation the module author used to write a shorter table.insert in their code (I’ve even seen local append = table.insert in the wild!). And then again, the abbreviation doesn’t gain us much: being comfortable to read is more important than being comfortable to write, but being easy to maintain is just as important if not more.

And yes, it is important to ponder what are the differences between being “easy to read”, “easy to write” and “easy to maintain”. And when pondering those, watch out for misleading short variables in the examples!

Of course, some idioms are advisable specifically for when you have short variables:

local r, g, b = 0, 255, 0

Everyone can easily read what’s going on there. But note that, almost without noticing, I also used a realistic example here! Realistic examples help getting the discussion grounded, and I find that they are often lacking when discussing syntax.


PS: And before someone mentions, the performance gains for localizing such variables as local tinsert = table.insert are overstated:

🔗 On the word “latino”

One of my least-favorite American English words is “latino”, for two reasons:

First, a linguistic reason: because it’s not inflected when used. When you’re used to the fact that in Spanish and Portuguese “latino” refers only to men and “latina” only to women, hearing “latino woman” sounds really weird (weirder than, say, “handsome woman”). Even weirder “latino women”, mixing a Spanish/Portuguese word and English grammar. “Bonito girls”? :)

Second, a sociological reason: because using a foreign loanword reinforces the otherness. Nobody calls the Italian community in America “italiano”, although that’s their name in Italian. The alternative “Hispanic” is not ideal because it doesn’t really make sense when including Brazil, which was never a Spanish colony (plus, the colonial past is something most countries want to leave behind).

I can’t change the language by myself, so I just avoid the term and use more specific ones whenever possible (Colombians, Argentines, Brazilians, South Americans, Latin Americans when referring to people from the area in general, etc.)

After writing the above, I checked Wikipedia and it seems the communites in the US agree with me:

« In a recent study, most Spanish-speakers of Spanish or Hispanic American descent do not prefer the term “Hispanic” or “Latino” when it comes to describing their identity. Instead, they prefer to be identified by their country of origin. When asked if they have a preference for either being identified as “Hispanic” or “Latino,” the Pew study finds that “half (51%) say they have no preference for either term.”[43] A majority (51%) say they most often identify themselves by their family’s country of origin, while 24% say they prefer a pan-ethnic label such as Hispanic or Latino. Among those 24% who have a preference for a pan-ethnic label, “‘Hispanic’ is preferred over ‘Latino’ by more than a two-to-one margin—33% versus 14%.” Twenty-one percent prefer to be referred to simply as “Americans.” »

I think the awkwardness in the grammar from point one actually reinforces point two, because it strikes me as something that no Spanish or Portuguese native speaker would come up with by themselves. So it sounds tacked upon.

Don’t get me wrong, I fully identify as a Brazilian, a South American and a Latin American — travellling abroad helps a lot to widen your cultural identity! — and I have no problem when people wear the term “latino” proudly, but I always pay close attention to the power of language and how it represents and propagates ideas.

🔗 String interpolation in Lua

Lua is known for having a very lean standard library, and for providing mechanisms to do things instead of a ton of features.

String interpolation isn’t available out of the box, but doing it in Lua isn’t a new trick. In fact, the manual includes it as an example of string.gsub:

local t = {name="lua", version="5.3"}
x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t)
--> x="lua-5.3.tar.gz"

This applies to members of a table only, though. Python is introducing a general string-interpolation syntax:

a = "Hello"
b = "World"
f"{a} {b}"
f"{a + ' ' + b}"

Given that Lua supports the f"str" syntax for functions with a single string argument, I thought it would be nice to put its Lua-provides-the-mechanisms ethos to test by trying to write my own Python-like f-string formatter.

And here it is, in all its 28-line glory (and I went for readability, and not to write it as short as possible):

function f(str)
   local outer_env = _ENV
   return (str:gsub("%b{}", function(block)
      local code = block:match("{(.*)}")
      local exp_env = {}
      setmetatable(exp_env, { __index = function(_, k)
         local stack_level = 5
         while debug.getinfo(stack_level, "") ~= nil do
            local i = 1
            repeat
               local name, value = debug.getlocal(stack_level, i)
               if name == k then
                  return value
               end
               i = i + 1
            until name == nil
            stack_level = stack_level + 1
         end
         return rawget(outer_env, k)
      end })
      local fn, err = load("return "..code, "expression `"..code.."`", "t", exp_env)
      if fn then
         return tostring(fn())
      else
         error(err, 0)
      end
   end))
end

It works just like the Python example:

a = "Hello"
b = "World"
print(f"{a} {b}")

Unlike the one-liner from the Lua manual, it also works with local variables:

local c = "Hello"
local d = "World"
print(f"Also works with locals: {c} {d}")

do
   local h = "Hello"
   do
      local w = "World"
      print(f"Of any scope level: {h} {w}")
   end
end

Some more demos:

print(f"Allows arbitrary expressions: one plus one is {1 + 1}")

local t = { foo = "bar" }
print(f"And values: t.foo is {t.foo}; print function is {_G.print}")

local ok, err = pcall(function()
   print(f"This fails: { 1 + } ")
end)
print("Errors display nicely: ", err)

If there’s interest, I can make this a module in LuaRocks (probably calling it F rather than f).

Update! This is now available in LuaRocks as a module! Install it with:

luarocks install f-strings

More info at the f-strings GitHub page. Enjoy!


Latest posts


Search


Admin area


Feeds