Lua as a Catlang Host

While it has it's quirks, it has some appealing features:

These can each be useful when implementing a concatenative programming language. Especially a language that takes after Factor or Joy. Both of these are higher-order functional programming languages. Thus, first-class functions will be a boon.

Variadic functions and multiple returns means generated stack-based code can have a more direct mapping to the underlying langauge. This isn't so say there won't be some work needed to handle calling lua functions from a stack language. You will need some mechanism for annotating how many args to give to a native-lua function.

Finally, Lua can be found in many places. It's a minimal language meaning a compiler doesn't need to handle a complex grammar to generate valid code. It's portable meaning it can be moved to various enviroments. And finally, it's embeddable meaning the language can be found in many useful runtimes (LÖVE, Pico-8, OpenResty, nvim, Defold, and more).

Additionally, Lua has easy access to coroutines which could provide a nice means for concurrency.

Primary Source of Inspiration

This isn't an idea I had on my own. But something I stumpled across finding an old thread on the lua-users archive:

Patrick Mc Writes:

Thanks Malkia. Yes I had trouble naming my Son and Daughter on top of this :)

Thanks Ralph and Axel.

I was trying not to clutter the list as I usually do but I can't explain the motivation quickly. The motivation for all of this to bring a new paradigm to C/Lua and hopefully build an abstraction layer over the C API. I started another thread due to the change in topic. I am not certain the premise is logical yet.

I have fallen in love with the concatenative programming paradigm. I spent a month with the Factor language. It is extremely neat but at it's current trajectory it will have been many years to move from 0.9 to 1.0, the state of similar languages is much worse.

So I was thinking, do we have to have another language for this? Lua gives us a prebuilt stack and functions don't have to take arguments and return values.

This kind of code(pseudo example):

stack_on(1, 1); add(); --> 2

might be slower but perhaps this style of programming could lead to useful code like:

quote_on(1, 5, 9, 4, 10, ); is_even() --> 4, 10

However Factor is damn near impossible to learn. Javascript might have document.blah.blah.getElementById("myHeader") but at least you know where everything lives. With these languages how do you know that dup comes from the kernel library?

So what I was thinking is that every word should be prefixed by the library it is called from but that a short form would be needed and a 100% consistent scheme would be needed too. factor has about 26K function(words actually). These tiny function can be used to build other tiny functions very quickly but again without a naming convention it becomes very hard to follow along with.

So this is why I was thinking k_dup from kernel1 or io_read() from io2, the number at the end would match the truncation of the prefix. I think this is butt ugly but quite logical.

I don't have enough experience with the C API to know if this is nonsense yet, any feedback would be great.

Thanks-Patrick

Peter Sommerfeld replies:

I have fallen in love with the concatenative programming paradigm.I spent a month with the Factor language.

Indeed an interesting language...

So I was thinking, do we have to have another language for this?

Lua is well suited for domain specific languages or playing with different paradigms. Eduardo Ochs has bootstrapped a Forth in 40 line of Lua (see "Lua programming gems").

factor has about 26K function(words actually). These tinyfunctioncan be used to build other tiny functions veryquickly but againwithout a naming convention it becomes veryhard to follow along with.

Factor has introspection facilities. I don't like the idea of prefixing everything. Why write math.floor() if floor() will do ? One table lookup more. Of course, it depends on...

Peter

David Hollander replies:

Lua can be considered a "right to left" concatenative language as-is by using "..." . Translation of the Factor tutorial:

function dup(x, ...)
    return x, x, ...
end
function reverse(x, ...)
    return (x):reverse(), ...
end
function normalize(x, ...)
    return x:gsub('[^%w]',''):lower(), ...
end
function is_eq(x, y, ...)
    return x==y, ...
end
function is_polynomial(x, ...)
    return is_eq(reverse(dup(normalize(x, ...))))
end

assert(is_polynomial 'hello' == false)
assert(is_polynomial 'race car' == true)
assert(is_polynomial 'A man, a plan, a canal: Panama.' == true)
                

Errr maybe that's "left to right". Depends on one's perspective I suppose.

- David

Another source of interest: Bootstrapping a Forth

Another source of inspiration was "Bootstrapping a Forth in 40 lines of Lua code":

Due mostly to cultural reasons, Forths tend to be built starting from very low-level pieces: first the inner interpreter, in Assembly or C, then the basic libraries and the outer interpreter, in Forth bytecodes, or - rarely - in C. We take another approach. If we consider that Lua is more accessible to us than C or Assembly - and thus for us Lua is “more basic” - then it is more natural to start from the outer interpreter, and the dictionary only has to have the definition for one word, one that means “interpret everything that follows, up to a given delimiter, as Lua code, and execute that”. An outer interpreter like that fits in less than 40 lines of Lua code, and it can be used to bootstrap a whole Forth-like language.