Charlie Harvey

Lua — Day 2

The second day of the Lua introduction is certainly heavier going than the first. We are introduced properly to the data structure that is at the heart of Lua, the table.

Tables are efficiently implemented datastructures that can work like arrays or like hashes or like both at the same time. Lua also allows you to change the behaviour of tables using metatables.

This is a powerful concept that allows us to implement our own OO system from the ground up. Lua gives you just enough to work with to make this not only possible, but syntactically slightly pleasant. The other big topic today was Lua’s concurrency system. It uses coroutines to enable multitasking. Again this is a very minimalist approach, and requires some work by the programmer.

The day is rounded off with an interview with the language creator, Roberto Ierusalimschy (pictured). He taks about the trade-offs inherent in designing programming languages and identifies Lua’s sweet spot as being scripting, saying

Roberto Ierusalimschy speaking at the Lua Workshop Toulouse 2013

Nowadays, most people use "scripting language" as almost a synonym for dynamic language. But scripting has a more specific meaning, of a language to … "glue," software that is frequently written in a different language … Lua has always been developed with this kind of use in mind.

My experiences

I liked working with Lua’s simple, prototype OO system. It felt like I was really getting my hands dirty. Similarly with metatables. This sort of under-the-hoodness is great in a scripting language, but I would worry about building larger systems in this sort of way. It feels like it might blow up at any moment.

There were a few syntactical choices which I wasn’t so keen on.

Hey Lua, ALGOL-68 called. It wants its array indexing system back!
  • The 1-indexed arrays — "Hey Lua, ALGOL-68 called. It wants its array indexing system back!" — that is just a matter of taste.
  • Having to write self.variablename when talking about class variables. You will see some ugly code in my solutions to the exercises.
  • I didn’t like the fact that the order in which you define functions and variables can be significant. I want to be able to refer to things I haven’ yet defined.

Another thing that would be nice to have is completion in the REPL. I guess that I have been spoiled by spending time in GHCi which is very nice. Installing rlwrap helped make the history features of readline work as expected, which is something I guess.

Other than that Lua has been nice to work with and I feel like I am getting my head around the language pretty well.

Exercises

Easy exercises

Write a function called concatenate(a1,a2) that takes 2 arrays and returns a new array with all the elements of a1 followed by all the elements of a2.

Trivial. Lua didn’t get in the way at all here. function concatenate (a1, a2) for i=1,#a2 do a1[#a1+i] = a2[i] end return a1 end

Our strict table implementation in Reading and Writing on p19 doesn’t provide a good way to delete items from the table. If we try the usual approach treasure.gold = nil, we get a duplicate key error. Modify strict_write to allow deleting keys (by setting their values to nil)

Again, the easy in the exercise titles seems appropriate. I simply modified the first line of the function so that instead of reading: if _private[k] then It read: if _private[k] and v ~= nil then

Medium exercises

Change the global metatable you discovered in the Find section earlier so that any time you try to add 2 arrays using the plus sign (eg. a1+a2), Lua concatenates them together using your concatenate function

This one stretched my brain. I am still not sure if my solution was what the author was looking for. It took me a few hours, because I was thinking that I ought to be able to change the add function in the global environment metatable (G). When I realized that variables are just elements in the global table, and that they would have their own metatables, I saw that I could override the _newindex function in _G to add a new metatable to tables as they were created, things made sense.

This is by no means a perfect answer, as tables decared with local scope (i.e. local t = {}) won’t have the metatable applied to them. But it does work and to be honest my brain hurt by this point! function newindex (t, k, v) rawset(t,k,v) if type(v) == "table" then -- print "setting mt" setmetatable(t[k] , { add = concatenate , _tostring = dumper.write } ) end end
setmetatable(
G,{
newindex=newindex})

Using Lua’s built-in OO syntax, write a class called Queue that impements a FIFO queue as follows.

q=Queue.new() returns a new object
q:add(item) adds item past the last one currently in the queue
q:remove() removes and returns the first item in the queue or nill if it is empty

Compared to the previous question, this was a breeze and implemented in about 15 minutes. I used the Penlight library’s pretty printing capabilities here for debugging purposes. dumper = require "pl.pretty"
Queue = { q = {} }
function Queue:new() local obj = { } setmetatable(obj, self) self.
_index = self return obj end
function Queue:add(item) if item == nil then error "Can't add nil item to queue" end self.q[#self.q+1] = item end
function Queue:remove() if #self.q<1 then return nil else fst = self.q[1] for i=1,#self.q - 1 do self.q[i] = self.q[i+1] end self.q[#self.q] = nil return fst end end
function Queue:show() dumper.dump(self.
q) end

Hard exercise

Using coroutines, write a fault tolerant function retry(count, body) that works as follows

Call the body function
If body yields a string with coroutine.yield(), consider this an error message and restart body from the beginning
Don’t retry more than count times; if you exceed count, print an error message and return
If body returns without yielding a string, consider this a success.

Again, this seemed way easier than the metatables exercise. Perhaps I missed something or creating a new coroutine per try is not what the author had in mind. I have added a debug variable at the top of the code as a convenience when I was building it — it toggles the display of my debug messages. local debug=false
function retry(count, body) local tries = 0 for i=1, count do tries = i _,res = coroutine.resume(coroutine.create(body)) if type(res) ~= "string" then break else if(debug) then print("Err: " .. res) end end end if(tries>=count) then if(debug) then print("Failure after " .. tries .. " attempts") end return end if(debug) then print("Succeeded after " .. tries .. " attempts") end end
retry( 5 , function() if math.random()>0.2 then coroutine.yield("something bad happened") end print "Success!" end )

Wrapping up

So, that’s it for day two. Lua is a nice dynamic, scripting language so far. It is very minimalist and fundamentally structured around the simple but powerful table data structure.

Image of Roberto Ierusalimschy speaking at the Lua Workshop Toulouse 2013 from Wikipedia, taken by Alexander Gladysh and used under a Creative Commons licence.


Comments

  • Be respectful. You may want to read the comment guidelines before posting.
  • You can use Markdown syntax to format your comments. You can only use level 5 and 6 headings.
  • You can add class="your language" to code blocks to help highlight.js highlight them correctly.

Privacy note: This form will forward your IP address, user agent and referrer to the Akismet, StopForumSpam and Botscout spam filtering services. I don’t log these details. Those services will. I do log everything you type into the form. Full privacy statement.