This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Lemur's profile picture

Functional Lua

Started by Lemur, 10 September 2014 - 01:00 AM
Lemur #1
Posted 10 September 2014 - 03:00 AM
Some amazing initial work by Shimomura Ikkei that I found, but I had the idea to add a few more things to it. Currently working on porting over various methods from Ruby, this is a very early version of what can be done.

http://pastebin.com/NDwsNrZj

I'll probably start namespacing these under something like Enumerable to keep it out of the global namespace. Any opinions so far on it?

Future plans:
  • See about pattern matching
  • Monadic composition starting with Maybe
  • List Comprehensions
  • More powerful piping and streaming ala Clojure and Elixir
Admittedly if I find how to monkeypatch arrays, I indend to fix them up a bit so I can do things like:

{1,2,3,4}.each(function (x) print(x + 1) end) -- returns the table unmodified, but executes code for each item
{1,2,3,4}.map(function(x) return x * 2 end) -- returns {2,4,6,8}
{1,2,3,4}.reduce(operator.add) -- returns 10
{1,2,3,4}.partition(function(x) return i % 2 == 0 end) -- returns {{2,4},{1,3}}

That'd be something for language readability for sure. As to how to pull that off, I may well make a global method called enumeration that takes in a table and returns one with the methods tagged on it. Makes for a slight pain iterating, but I'll have to look for a better solution…

Thoughts?

EDIT So this is a thing: https://github.com/mirven/underscore.lua

No real need to continue given that, unless I can magically find how to monkeypatch to the above code.

EDIT(again) ok, so you can do that (kinda), go figure. Props to mirven for sure, handy, this. Minified version should be a tinge more practical, but still a load for sure.

underscore.lua (10,326b) - http://pastebin.com/4UK502Jb
underscore.min.lua (6,035b) - http://pastebin.com/XBQZcVeK
Edited on 10 September 2014 - 03:23 AM
Bomb Bloke #2
Posted 10 September 2014 - 06:22 AM
Some tweaks you may or may not want consider:

With do_times, I'd be tempted to have it store any returned results from func in a table, then return the unpacked table when done.

In that same function, bear in mind that "for" loops repeat until the counter variable would exceed the target value. "for i = 0, 1 do" performs two iterations, for example.

"pipe" could be re-written as:

function pipe(value, ...)
  for i, v in ipairs(arg) do value = v(value) end

  return value
end

Rather than having "select" call "filter" (or "any_are" call "find"), it's more efficient to just copy the function pointer, eg:

select = filter

In regards to Shimomura's code, you might consider changing his use of table.getn:

#myTable returns the highest index before reaching an index with a value of nil. Of the three it's by far the fastest, but if there are "holes" in your table it may not be useful.

table.getn(myTable) usually returns the highest index in the table regardless of nil's, but if the table is constructed a certain way it can be confused.

table.maxn(myTable) reliably returns the highest index in the table, but is slower than the other two options.

Say you have myTable set to {4,nil,5}. #myTable will be 1. table.getn(myTable) may be 1 or 3, depending on how the table reached that state. table.maxn(myTable) will be 3.

To operate in the way his example suggests it should, his "select" function should be re-written as:

-- filter(function, table)
-- e.g: filter(is_even, {1,2,3,4}) -> {2,4}
function filter(func, tbl)
  local newtbl= {}
  for i,v in ipairs(tbl) do
    if func(v) then newtbl[#newtbl+1]=v end
  end
  return newtbl
end

… though this breaks for tables with non-numeric keys, so it may be that you don't want the example's behaviour. In any case, much the same applies to your "reject" function.

Note that newtbl[#newtbl+1] is faster than using table.insert() if you just want to tack something onto the end of a table. Certainly your "partition" function would benefit from that method.