Article Series

This article series discuss more than 30 different programming languages. Please read overview before you read any of the details.

Playing with Records Related Articles.

Where to Discuss?

Local Group

Preface

Goal: Continue Part One.


5: More Functional

However, I am still curious about using fun library.

Flatten Module

Of course flatten can be done using foldl. But the code looks long, so I’d better put in separate library.

function extract(songs)
  return map(
    function (song) return song.tags end, songs)
end

function clean(tags)
  return filter(
    function (tag) return tag end, tags)
end

function flatten_rec(init, tags)
  return foldl(
    function(acc, tag)
      if type(tag) == 'string' then
        acc[#acc + 1] = tag
        return acc
      end
      if type(tag) == 'table' then
        return flatten_rec(acc, tag)
      end
    end,
    init, tags
  )
end

function flatten(tags)
  return flatten_rec({}, tags)
end

I do not feel pleased with the length of the code. But here we are. I have to make peace with Lua limitation.

Using Flatten Module

Now we can have a very short code.

local inspect = require('inspect')
require 'fun' ()
require 'my-songs'
require 'my-flatten'

print(inspect( flatten( clean(extract(songs)) ) ))

With the result similar as below:

$ lua 09-flatten.lua
{ "60s", "jazz", "60s", "rock", "70s", "rock", "70s", "pop" }

Of course with hidden implementation, in module.

Exclude Algorithm

To get a unique array, we need to create exclude function first.

This come in my mind, inspired by haskell algorithm.

exclude :: String -> ([String] -> [String])
exclude tag = filter((/=) tag)

unique :: [String] -> [String]
unique [] = []
unique (tag:tags) = tag:unique(exclude tag tags)

tags :: [String]
tags = ["rock", "jazz", "rock", "pop", "pop"]

main = print (unique tags)

Exclude in Lua

We can just implement the filter using fun filter library, and get about similar result with Haskell.

local inspect = require('inspect')
require 'fun' ()
require 'my-songs'
require 'my-flatten'

function print_inspect(tags)
  print(inspect(tags))
end

function exclude(val, tags)
  return filter(
    function (tag) return tag ~= val end, tags)
end

local tags = flatten( clean(extract(songs)) )
each(print_inspect, songs)

The result is as below:

$ lua 10-exclude.lua
"60s"
"jazz"
"60s"
"rock"
"70s"
"rock"
"70s"
"pop"

Generator Issue

We can try to change a bit, to make the reesult oneliner

print_inspect( exclude("rocks", tags) )

But the result is a loooong code:

$ lua 10-exclude.lua
{
  gen = <function 1>,
  param = { <function 2>, <function 3>, { "60s", "jazz", "60s", "rock", "70s", "rock", "70s", "pop" } },
  state = 0,
  <metatable> = {

Lua: Generator in Inspect

We can have a look at the type with this line:

print( exclude("rocks", tags) )

Now we know the problem. This is no longer simple array, but generator.

$ lua 10-exclude.lua
<generator>	table: 0x5640f3c62870	0

That’s it.

Tools Module

We can fix this by using custom normalize function. For convenience, I put this in separate module.

local inspect = require('inspect')

function normalize(tags)
  if type(tags) == 'table' then
    local acc = {}

    function add_array(new)
      acc[#acc + 1] = new
    end

    each(add_array, tags)

    return acc
  end
  
  return tags
end

function print_inspect(tags)
  print(inspect(normalize(tags)))
end

Each inspect time, I normalize the array first.

Merge Table

I also add merge table capability.

function join_tables(t1, t2)
  for k,v in ipairs(t2) do table.insert(t1, v) end return t1
end

Seem like Lua lack of tools. But it is the a lightweight language is.

Unique (Distinct)

Now we can peacefully, porting algorithm, from previously in Haskell to Lua.

local inspect = require('inspect')
require 'fun' ()
require 'my-songs'
require 'my-flatten'
require 'my-tools'

function exclude(val, tags)
  return normalize(filter(
    function (tag) return tag ~= val end, tags))
end

function unique(tags)
  if type(tags) == 'table' then
    if #tags == 0 then return {} end
    if #tags == 1 then return { head(tags) } end

    return join_tables(
      { head(tags) },
      unique( exclude(head(tags), tail(tags)) )
    )
  end
end

local tags = flatten( clean(extract(songs)) )
print_inspect( unique(tags) )

With the result similar as below:

❯ lua 11-unique.lua
{ "60s", "jazz", "rock", "70s", "pop" }

Lua: Flatten and Unique (Distinct)

Despite the mess I wrote above. It has been very interesting to work with Lua. I’m looking forward to explore more Lua.


6: Concurrency with Coroutine

Yes, Lua is capable of concurrency. But not in parallelism context.

My code below is based on the official documentation from the Programming in Lua first edition book.

We can, rewrite our previous flatten code with actor pattern, using sender and receiver.

Reference Reading

Prepare

First thing first, the dependency.

local inspect = require('inspect')
require 'my-songs'

Sender

Consider rewrite the sender from example above to suit our custom song task.

function sender ()
  return coroutine.create(function ()
    for _, song in pairs(songs)
    do
      if type(song.tags) == 'table' then
        for _, tag in pairs(song.tags)
          -- send messages
          do coroutine.yield(tag) end
      end
    end
  end)
end

_.

Lua: Concurrency with Coroutine: Sender

Receiver

And also onsider rewrite the receiver from example above to suit our custom song task.

function receiver (prod)
  local tags = {}

  while true do
    -- receive message
    local status, message = coroutine.resume(prod)

    if message==nil
      then break
      else tags[#tags + 1] = message
    end
  end

  return tags
end

Running Both

Pretty short right!

Consider gather both function.

prod = sender()
tags = receiver(prod)
print(inspect(tags))

With the result similar as below array:

$ lua 12-coroutine.lua
{ "60s", "jazz", "60s", "rock", "70s", "rock", "70s", "pop" }

Lua: Concurrency with Coroutine: Receiver

That is all.


Real Life Lua

You might want to have a look of what Lua can do. I have made an article series about AwesomeWM configuration, written in Lua.

Awesome WM - Overview


What is Next 🤔?

Consider continue reading [ Julia - Playing with Records - Part One ].