What makes Lua tick?
lua-users.org
lua-users.org
But your average Rubyist/Pythonista shouldn't be fooled. The ecosystem of Lua assumes you eat C for breakfast, and if you don't, you're going to be looking around for easy, plug-and-play libraries and wondering why they don't exist.
They don't, because in Lua world, you either call C from Lua or embed Lua in C. You don't throw something together in Lua using the happy friendly libraries that others have written for your convenience (often, in C).
I see comparisons being made with Lisp here and there. The most truthful comparison is sociological: as with the Lisp community, the Lua community (which is very friendly, approachable, and helpful) largely assumes you're smart enough to build it yourself. In C.
If that frightens you, you should probably go back to whatever you were doing before.
What can be done to address that?
Lets consider Lua. Lua uses a prototype based inheritance system and coroutines for concurrency and has basically no standard library. Anyone who uses Lua extensively ends up building their own object/class system in addition to filling out the standard library. This makes sense; an embedded language shouldn't force its object model on its host and it doesn't need a standard library. The host most likely comes with a standard library of its own. The lack of common building blocks and the reliance on external code means Lua will never be a general purpose scripting language because its just too damn hard to reuse code.
I'm not sure I agree. What you want for embedding is something small, fast, and easily interoperable. What you want for general-purpose programming is powerful, composable abstractions. Lua meets both sets of criteria. It does so by means of exceptionally good design. The fact that it doesn't have the same object model as some other languages doesn't really prove anything, does it? Every well-designed programming language deserves to be thought of on its own terms.
Not having a featureful standard library, on the other hand, certainly is the kind of tradeoff you're talking about. But (a) is it so important to have a standard library as opposed to just pulling in the libraries one needs? and (b) you may be overestimating the difficulty of interfacing with external code in Lua. It's much easier than with other dynamic languages. "Too damn hard" seems an overstatement.
The big question with Lua is whether it's better enough than its peers to overtake them in any area other than embedding. If it is, we should see some sort of killer application of Lua. There is momentum toward an embedded-web-app style a la OpenResty, but also still a high barrier to entry; for one thing, it isn't well documented.
That's a bit of an exaggeration. There isn't anywhere near the number of libraries that Python and Ruby have, nor anywhere near the general level of quality, but you can do an awful lot without ever touching C. Reams of Lua code have been written to script WoW by people who have no idea how to program in C. In fact, one of the primary design objectives of Lua is to have a language that's simple for non-programmers to learn.
> you're going to be looking around for easy, plug-and-play libraries and wondering why they don't exist.
That's very true. I spent the better part of a year working on open source web development libraries in Lua but eventually gave up because nobody was using them. There are fantastic libraries in Python and Ruby, at this point it's not really worth reinventing the wheel just to do the same stuff, but in Lua.
The interesting thing about the Lua community is that very few people use Lua as their primary day-in-day-out language, which leads to a distinct lack of fanboyism. I think that's in part why so few libraries exist. The typical Lua programmer attitude seems to be, "Why should I rewrite that in Lua when I can just use the one written in <insert language here>?"
That plays into one of the reasons why there's not a lot of Lua libraries: they're mostly ecosystem-specific. There's quite a few Lua libraries for WoW that aren't inherently tied to WoW, but they aren't packaged in a way that makes them usable anywhere else. People writing code for a specific program that happens to use Lua generally can't use libraries from LuaRocks, so there's no reason for them to care about LuaRocks in any way.
Sadly I'm not sure that there's really any solution to this problem other than just accepting that an embedded language will have a very fragmented ecosystem.
Yes. The fact that LuaRocks is close to its 300th library illustrates that :)
I am one of the few people who use Lua as their main language, but I still turn to something else for things like Web development (Python + Flask). Maybe this will change with the OpenResty + Lapis stack [1], which is IMO the most interesting thing in Lua Web development since Mercury [2].
[1] http://leafo.net/lapis/ [2] https://github.com/nrk/mercury
Lua actually has lot of libraries dedicated to interoperability with other languages in a system (protobuf, ZeroMQ, ...), which makes it usable as part of a SOA, so you can write your web frontends in something else and still have most of your backend in Lua.
That said, I would still love to use it if it was even close to ready.
OpenResty itself, on the other hand, is perfectly usable and used in production at large websites such as Taobao (the Chinese eBay / Amazon) and CloudFlare.
It just happen that for most projects any of.them are "right" except the most esoteric ones...
You can fully pull some crazy "clever" stuff, but requires effort, usually the first idea in your head is already good enough.
list = ["1", "2", "3"]
sum1 = sum(int(num) for num in list)
sum2 = reduce(map(list, int), operator.add)
sum3 = 0
for num in list:
sum3 += int(num)
Lua only has the third of these three options. I don't really consider this a particularly compelling advantage as a user.At least, I know I sure spend a lot more time reading code than I do writing code; so I appreciate any language that tries to optimize for that use-case and helps me minimize my cognitive overhead.
function curry(f,x)
return function(...)
return f(x, ...)
end
end
function reduce(f,t,r)
r = r or t[1]
for i=2,#t do
r = f(r,t[i])
end
return r
end
addf = function(a,b) return a+b end
sum = curry(reduce, addf)
t = {"1", "2", "12"}
print(reduce(addf, t)) -- 15
print(sum(t)) -- 15
res = 0
for i=1,#t do
res = res + t[i]
end
print(res) -- 15
res = 0
for k,v in ipairs(t) do
res = res + v
end
print(res) -- 15But this simplicity can have a downside, as there might not be canonical ways of doing things that users are accustomed to. For example, object orientation. It could be a hard sell to someone coming from an object-oriented language, when you have to understand, and choose between multiple different styles of object-orientation and implement them consistently in your project, or use one of the many libraries made just for this. For example, see http://lua-users.org/wiki/ObjectOrientedProgramming
Lua really is simple, but it not always easy.
* Lexical scoping * Tail-call optimization * First class functions / closures
Lua is not a functional programming language but I tend to have a large number of closures and recursive functions in my Lua code. Lua's limited feature set means that you end up using closures as a replacement for things which are special features in other language.
Here is an example of a closure doing the job of C/C++'s function-scope static:
View.Move = (function()
local move = {
[Direction.up] = View.Up,
[Direction.left] = View.Left,
[Direction.right] = View.Right,
[Direction.down] = View.Down
}
return function(self, direction) move[direction](self) end
end)()
Note that a closure is more efficient than function-scope static here. The static requires a "is initialized?" check every time the function gets called, the closure does not.Is Closure(function ... end) really an improvement over the normal IIFE look of (function ... end)()?
Now that you say it.. no, it's not. Changed.
do
local move = {
[Direction.up] = View.Up,
[Direction.left] = View.Left,
[Direction.right] = View.Right,
[Direction.down] = View.Down
}
function View.move(self, direction) move[direction](self) end
end debug.setmetatable(nil, {__call = function() return nil end, __index = function() return nil end})
f = nil
print(f(f.bar.baz)()()()) -- nil
This seems like an especially awful idea to me, though.That's why it's better to be bold and crash spectacularly, instead of trying to cover up unexpected problem by guessing. If you have an unexpected null pointer, there is a reason, and your expectations were wrong, so there's something bad going on that you don't know about. By silently hiding the problem, your program may be doing the just wrong thing, and you never know about it.
That's the problem with PHP, which has it even worse than Objective C. Ignoring errors and guessing what to do is PHP's way of life, and it's been the cause of countless security holes, data corruptions, and hard to diagnose bugs.
Use lots and lots of pedantic asserts, and test thoroughly, so the production code does not have to be riddled with labyrinths of error handling and mitigation guessing code. Assume the APIs obey their contracts, and write APIs with strong contracts that do what they promise. If something might return a NULL, then test for it and handle it properly, so whoever is reading and maintaining your code knows that you considered the possibility and thought through the solution, and the computer doesn't have guess what to do.
Computers are notoriously bad at guessing the programmer's intentions, so programming languages should not encourage programmers to let the computer guess what they meant.
Is that similar to what JS does with undefined, or am I missing something?
I'd add that most worries about silent nil returns stem from not accepting Lua's very clear value semantics; nil literally means "undefined value" (as compared to JS's null or Python's None); setting a variable to nil is the idiomatic way to release its value for garbage collection.
So "silently returning nil" is more like "explicitly returning notice that the value is undefined". Whether you want that to be an error-raising-event or not is up to you, the programmer. And it's easy to control that error-throwing behavior in a granular way; you can attach the error-throwing metamethods to the globals table, or to specific tables you care to protect.
You can actually access said global environment as the _G variable.
Now, you could totally stick a metatable on _G that caused some sort of exception when you tried to get the value for a key with a nil value.
The issue was raised on the Lua mailing list, and the response from one of Lua's curators was, literally, "I used to make mistakes like that, but then I got used to it." Lua is admirable for its size and speed, but I believe that it's sometimes too conservative about changing historical behaviors to accommodate safer policy.
function a_stricter_table()
return setmetatable({}, {__index = function(t,k)
error("no value at "..tostring(k))
end})
end
t = a_stricter_table()
t.a = 5
print(t.a) -- 5
print(t.b) -- crash D:
A similar trick can make trying to unset a value by assignment raise an error, while unsetting a value using a delete() function is allowed.Edit: Removed the fake table/backing table thing, since you can do the one trick without it. I think the other trick requires it, though.
For example, this is how you implement optional arguments and default values in Lua:
NeumannNeighborhood = function(position, extent)
extent = extent or 1
...
end
Here extent is optional and its default value is 1. That's how you keep a language small.Suppose that you accidentally wrote:
NeumannNeighborhood = function(position, extent)
extent = extant or 1
...
end
It would always be assigned 1, and you'd never be told about the error.And as I said bigger Lua project usually use a script which prevents the use of undeclared globals. Such a script would detect the bug in your example because "extant" is a global and undeclared (variables are global by default in Lua).
No, it isn't. No value is ever assigned to "extent" if the function gets called without a second argument. "extent" is nil just like all other non-existing objects are nil.
It is a common newbie mistake to treat Lua's nil as the equivalent of a null pointer or some kind of "nothing value" you find in other languages.
Lua's nil is not a value of any kind. "object = nil" means "delete object" not "set object to nothing value". Because of that you should never assign nil to anything unless you actually want to delete it. Delete as in "garbage collector please pick this up". E.g.
character.armor = NULL
in C does not delete the armor field of the character struct. character.armor = nil
in Lua does.. and you probably don't want that in such situations because if you handle it this way you create countless unnecessary allocation, reallocation, and deallocation operations behind the scenes. If you just mean "nothing here right now" you should set the field to "false" or some specific nothing value.>If the value of "extent" was retrieved from a table argument using an incorrect key, the mistake would go unreported.
That is correct and a problem indeed. However, in my experience such things rarely happen and the resulting bugs are easy to find.
If you prefer the Lua concept that all possible elements start out pre-set to nil, then again, "extent" is already defined. Whichever mental approach you prefer, the variable "extent" exists in your example.
> Lua's nil is not a value of any kind. "object = nil" means "delete object" not "set object to nothing value". Because of that you should never assign nil to anything unless you actually want to delete it. Delete as in "garbage collector please pick this up". E.g.
This isn't correct. Nil is a value type, and it exists solely to be different from any other value. Lua tables are presented as containers in which every possible key starts out already assigned the value of nil. Nil doesn't explicitly mean "delete object". Rather, a table variable is a reference to a table object, so assigning it a value of nil removes the reference and makes the object available for garbage collection (if nothing else is referencing it).
In other words, the reason Lua makes no distinction between non-existent elements and elements assigned to nil is that, conceptually, every possible element already exists and starts out assigned the value of nil. Therefore, when I use the term "non-existent", I'm speaking in practical terms from the perspective of the programmer. You presented to me an example which you claimed would no longer work under the behavior I proposed, but the "extent" variable would be both declared and defined in the function's argument list, so the "default argument" behavior would still work. The variable would just be assigned the value of nil if no argument was passed for it.
I'm not sure why you're offering these explanations except to make it appear as if I don't know Lua. I presume you're the one who voted down my previous comment.
> That is correct and a problem indeed. However, in my experience such things rarely happen and the resulting bugs are easy to find.
To the response that it just doesn't happen that often or that the bugs can be easily fixed, I would point out the existence of several Lua static analyzers and direct you to Google's own analyzer (http://code.google.com/p/lua-checker/), with this project description:
"Lua is dynamically typed, with a simple type model. Variables can be assigned values of any type. This makes development of small scripts somewhat easier, but it means that in larger programs many common programming errors are not detected until run time. For example:
- Referencing a variable that has not been declared will return nil. This is no different from referencing a 'declared' variable that has the value nil. Thus spelling mistakes in Lua variable names can go undetected and lead to program misbehavior.
- Tables (Lua's main data structure) are not typed, so any table can contain any keys. When tables are used in a manner similar to C structures, spelling mistakes in field names can go undetected and lead to program misbehavior.
- Function arguments and return values are also untyped and have similar problems.
In practice, Lua programs over (say) 1000 lines tend to accumulate these kinds of problems, making debugging difficult."
a = setmetatable({...}, {__index = function(i) error "undefined" end})
is all you need.
Nowadays I am working on a large 99% Lua code base and I love the language! Lua has a small set of building blocks and only if you try to write something non-trivial in Lua you realize how incredibly well these blocks fit together.
It's funny, Lua is the opposite of Go for me there. I was excited about Go after reading the tutorial, but then I tried to seriously use it the experience was underwhelming.
Edit: Apparently, OpenResty[2] is largely responsible for the impressive performance in those benchmarks, and Lua may be secondary.
1. http://www.techempower.com/benchmarks/#section=data-r6&hw=i7...
[1] https://github.com/TechEmpower/FrameworkBenchmarks/blob/mast...
I would be interested to hear anyone's experience using luaj on Android (thats where I plan to use it), and how it may compare to other options like Rhino.
http://partiallyappliedlife.blogspot.no/2009/06/lua-on-java-...
See also:
https://groups.google.com/forum/#!topic/android-developers/7...
and:
[3 days ago on HN] Dalvik vs. SpiderMonkey vs. Native https://news.ycombinator.com/item?id=6173757
Appcelerator originally did Rhino on Dalvik and moved to V8 after benchmarks showed Rhino was terrible. http://developer.appcelerator.com/question/133696/what-is-v8...
The idea was to use kahlua or luaj to do the same helper tasks within the main process so other Java instances wouldn't have to be spawned.
But for Android, plain old C based Lua works great with the NDK. There is also LuaJava and JNLua which can cross the native/Java bridge.
Some links:
- The Lunatik GSoC project - http://netbsd-soc.sourceforge.net/projects/luakern
- Marc Balmer's FOSDEM'13 paper - http://www.netbsd.org/~mbalmer/lua/lua_config.pdf
I gather that the principal virtues of Lua for this purpose are:
1. Lua tables have a nice syntax
2. Lua's load function is very fast
3. Lua has a small, well-tested trusted code base
4. It has a sane semantics for dynamic loading
I've been strongly tempted to use it for actual core app development but every time I look at it the scarcity of common libraries compared to something like Ruby or Python cools me off.
And luajit is more alien technology than common lisp :)
You can take a look at the UI code, since it's dumpable from the game client: http://wowprogramming.com/utils/xmlbrowser
Many more, but it is what come to my mind right now
While I cannot pretend it is significant, it is new/fresh/recent. I'm just going through a transition to the GMime mime library, but beyond that the code is stable and I've used the client for the past few month or so, replacing mutt as my main client.
Now that I'm using it I'm finding interesting things to do all the time, using Lua has allowed me to move from having a mostly-extensible client (mutt) to having a __very__ extensible client.
http://bsd.slashdot.org/story/13/02/16/2329259/netbsd-to-sup...
Lua's a great language, but the amazingly good implementation is as much (or more) a factor than the language itself.