Lua: Good, bad, and ugly parts
2015-04-15 10:46
603 查看
Lua: Good, bad, and ugly parts
from :http://notebook.kulchenko.com/programming/lua-good-different-bad-and-ugly-parts25 Mar 2012
I have been programming in Lua for about 9 months and it seemed like a good time to pause and reflect on my experience with it. I have used various languages over the years -- Perl (soaplite.com
and other projects, including my current consulting work), C (aDHCP/DNS server for Arduino anda
ping-pong juggling robot), JavaScript (experiments with Google Maps and canvas),MATLAB(ping-pong
juggling robot), and others, from Turbo Pascal to F# -- and it was interesting to see how Lua compares to the other languages I've worked with. I have done different types of projects in Lua: a remote debugger (MobDebug),
extending a LuaIDE(ZeroBrane Studio), a mobile app (LuaRemote), several
educational scripts (EduPack), and a demo of
drawing on browser canvas with Lua.
I have come across several detailed lists that mention good and not-so-good parts of Lua (for example,Lua
benefits,
why Lua,
why Lua is not more widely used,
advantages of Lua,
Lua good/bad,
Lua vs. JavaScript, and
Lua Gotchas), but I found that some of the features that tripped me or that I cared about were not listed, so I put together my own list. It is far from being comprehensive and some aspects of the language are not covered (for example, math and string libraries),
but it captures the gist of my experience with the language.
Good
Small:20000 lines of C code that can be built into a 182K executable interpreter (under Linux).
Portable: builds on any platform with anANSI C compiler. You can see it running on almost anything frommicrocontrollers
andLego Minstorms
NXT, to
game engines, to
mobile toolkits, to
game consoles, to a
browser (translated to JavaScript).
Embedded and extensible language that provides a straightforward interface to/from C/C++.
Sufficiently fast:
performs well comparing to other languages and has a
JIT compiler that noticeably improves performance on many tasks; those who may still be not satisfied with the performance, can implement critical parts in C and, given the ease of integration with C, still benefit from other good
aspects. [Updated 3/9/2013] replaced shootout results that are no longer available withbenchmarksgame.
Well documented:
reference manual,book,wiki,
6-page short reference and more.
Friendly and enthusiasticcommunity. Between the excellent documentation, the wiki, themailing
list, andStackOverflow, I didn't have any issues finding answers to my questions.
Clean and simple syntax suitable for beginners and accessible to non-programmers. Lua has borrowed most of its control syntax fromModula,
the descendent ofPascal, which was widely used in education as an introductory language. I still remember using early versions of Philippe Kahn's fast
and elegantTurbo PascalIDE.
Integrated interpreter: just run
luafrom the command line.
Native support for
coroutines to implementiterators andnon-preemptive multi-threading.
Incremental
garbage collector that has low latency, no additional memory cost, little implementation complexity, and support forweak tables.
Powerful heterogeneoustables that store values of any type (except
nil) and can be indexed by values of any
type (except
nil):
{1, 2, 5, foo = "bar", [func] = "something", ["some spaces"] = value()}.
Lexical scoping.
Functional programming withfirst class functions
andclosures.
Tail calls:
return functioncall().
Recursive functions don't need to be pre-declared:
local function foo() ... foo() ... end; note this doesn't work with
local foo = function() ... foo() ... end.
Functions return
multiple values:
return 1, 2, 3. The caller can expect any number of values returned: if less than three is expected, the rest is discarded and if more than three is expected, the rest is
nil-initialized.
Functions allow variable number of parameters with
function foo(...) local args = {...}; bar(param, ...) end.
Tables can be "unpacked" into a list of parameters with
unpack(or
table.unpackin Lua 5.2):
print(unpack({1, 2, 3}))prints
1 2 3.
Manipulating environments (
getfenvand
setfenvin Lua 5.1 and
_ENVmanipulation in Lua 5.2), which allows buildingsandboxes
among other things.
Assignment to a list of variables:
local a, b, c = 1, 2,
x, y = y, x, or
a, b = foo().
Multiline strings (using
[[...]]; can be enclosed with
[[...[=[...]=]...]]) and comments (
--[[...]]).
Semicolon as a statement separator is optional (mostly used to resolve ambiguous cases as in
a = f; (g).x(a)).
Overloading using
metatables.
Metaprogramming to do things from getting and modifying an abstract syntax tree to creating a new syntax for yourDSL.
The
forstatement has two forms:generic (loops over iterators:
for a in iter() do ... end)
and
numeric (loops over numbers:
for a = 1, 5, 0.1 do ... end); the numeric one supports all numeric values for steps (not just integers).
Syntactic sugar for function calls (
f'string',
f"string",
f[[string]], and
f{table}) and method calls (
obj:m()).
Simple yet powerfuldebug library.
Fast and powerfulJITcompiler/interpreter (LuaJIT)
which includesFFIlibrary and isABI-compatible with Lua 5.1 (this means that it can load binary modules compiled for Lua
5.1).
Different
Tables and strings are indexed from 1 rather than 0.Assigning
nilas a value removes the element from a table. This is consistent with returning
nilfor non-existing element, so it makes no difference whether the element does
not exist or exists with a value of
nil.
a = {b = nil}produces an empty table.
No integers as a separate numeric type;the number type represent real numbers. The next version of Lua (5.3) maychange
that.
No classes;
object-orientation is implemented usingtables andfunctions; inheritance is implemented using themetatable
mechanism.
Method calls are implemented using
object:method(args)notation, which is the same as
object.method(object, args)notation, but with
objectevaluated only once.
niland
falseare the only false values; 0, 0.0, "0" and all other values evaluate as
true.
Non-equality operator is ~= (for example,
if a ~= 1 then ... end).
not, or, andkeywords used for logical operators.
Assignments are statements, which means there is no
a=b=1or
if (a=1) then ... end.
No
a+=1,
a++, or similar shorthand forms.
No
continuestatement, although there is anexplanation and a number of alternatives, like using
repeat break until trueinside the loop to break out of or a
gotostatement introduced in Lua 5.2.
No
switchstatement.
Brackets may be required in some contexts; for example,
a = {}; a.fieldworks, but
{}.fielddoesn't; the latter needs to be specified as
({}).field.
A control variable in a loop is localized by default and is not available after the loop.
Limit and step values in the numeric
forloop are
cached; this means that in
for i = init(), limit(), step() do ... endall three functions
init,
limit, and
stepare called once before the loop is executed.
Conditionals and other control structures require no brackets.
Strings and numbers are automatically converted (if string is used where a number is expected and vice versa), but not in equality/inequality comparisons:
0 == "0"is
false,
{} ~= 1is
true, and
foo["0"]and
foo[0]refer to two different keys in the table; other relational operators generate errors on comparing values of different types.
Both commas and
semicolons can be used in table literals; both can be placed before the closing curly bracket as anoptional separator:
a = {a = 1, b = 2, }.
Smaller than expected number of components that are available "out of the box"; some people see this as "batteries not included". This is the other side of having a compact and portable core and
is well compensated by havingLuaRocks and libraries likePenlight.
Bad
Limitederror handling support (usingpcall andxpcall), although
some mayargue that it is sufficient and just needs some syntactic sugar and more feature support (like deterministic finalizers). The combination of
pcall
and
erroris quite powerful, especially given that
errorcan return anything (for example, a table) rather than just a string, but having
catch ... finallyconstructs may be cleaner and simpler to read in many cases.
Global scoping by default (this has beenpartially addressed in Lua 5.2, which has no globals).
There is a
strict module that requires all global variables to be initialized. I have not had many issues caused by uninitialized globals, but still put this one into the "bad" category as I once made a mistake of calling a variable "next" and not localizing it, whichcaused
an issue with an iterator in a completely different module as it overwrote thenext function used with iterators.
No
Unicode support (at the very least you don't get
string.lenand pattern matching functions to recognize Unicode characters); there is abinding toICU
library that implements full Unicode support. See also thismessage and follow-ups for a good summary of what is already supported and what modifications may
be required for
string.*functions.
Limited
pattern-matching support, although the included one is still quite powerful. After using Perl for over 15 years, I miss some of the regexp features (mostly look-aheads, optional groups
(group )?, and groups inside groups), but nothing that warrants
the additional complexity in the implementation. Those who need more power in their regexps can useLPeg and itsre
module.
No ternary operator;several alternatives are available. I usually end up using
foo = test and value1 or value2form with the caveat that
value2can be assigned if both
testand
value1end up being
false.
No
POSIX functions built-in. There is the
luaposix module, but it requires compilation, which is not always an option. I didn't miss this much, but I did come across a case where I needed to get/set an environment variable, and having access to
getenvand
setenvwould be
convenient [Updated 6/1/2012] As miko noted in the comments, there isos.getenv, but no corresponding
os.setenv.
No class/object finalizers. Lua provides finalizer functionality through the__gc metamethod, but it is available only
for userdata types (and not tables) and doesn't match the functionality provided by other languages, for example,DESTROY andEND
methods in Perl. [Updated 05/27/2012] There is an undocumentednewproxy feature in Lua 5.1 that allows implementation of finalizers on tables; Lua 5.2 removed that feature
as it added support for__gc metamethod on tables.
No yielding between Lua and C code:
coroutine.yieldcall across Lua/C boundary fails with
attempt to yield across metamethod/C-call boundary. I happened to come across this
error several times as I was doing async programming withluasocket and coroutines, but solved it using thecopas
module. This has been addressed in Lua 5.2.
No built-in bit operations in Lua 5.1. This is addressed in LuaJIT (BitOp) and Lua 5.2 (bit32), which both include bit libraries.
Ugly
Number of elements in a table is not easy to get and the result depends on how you do this (or what you mean by "length"). This is probably not surprising, given how powerful tables are in Lua andthe fact that they support flexible indexing (by numbers and any other Lua type except
nil). Tables in Lua have two parts: an "array/vector" part (generated with
t = {1, 2, 3}) and a "hash" part (generated with
t = {a = "foo", ["b"] = 2}); the two can be flexibly combined.
#tablereturns the length of the shortest "array/vector" part (without any gaps) and
table.maxn(t)returns the longest "array/vector" part (this function is removed in Lua 5.2).
The "hash" part doesn't have a defined length. Both parts can be iterated over using the
pairsmethod, which allows you to count the number of elements in them. However,
print(#{1, 2, nil, 3})prints 4 and not 2 as one may expect, whereas
print(#{1, 2, nil, 3, nil})prints 2. I'm sure there is a good reasonable explanation for this, but for now it is in the "ugly" bucket.[Updated 11/17/2012] As FireFly noted in the comments, in Lua 5.2 the length operator isonly
defined for tables that don't have holes in them.
returnstatement can't be used if it's not the last statement in a block; in other words,
function foo() print(1); return; print(2) endwill trigger an error
'end' expected...
or
unexpected symbol near <whatever statement you have after 'return'>(depending on whether you have semicolon after
returnor not). Not that anyone would want use this for anything other than debugging, but I got bitten by it couple
of times. I would have put this in the "different" category, but I find it inconsistent that I can't use
return, but can use
do return endin exactly the same place.[Updated 5/19/2012] This also applies to
breakstatement,
although in Lua 5.2
breakis
no longer required to be the last statement in a block.
Only one value is returned from a function if it's not the last one in a list; for example:
function f123() return 1, 2, 3 end function f456() return 4, 5, 6 end print(f123(), f456()) -- prints 1, 4, 5, 6 print(f456(), f123()) -- prints 4, 1, 2, 3
The related behavior of
returnis also affected by this rule:
return f456()returns three values, but
return (f456())returns only one value (note the extra pair of parentheses). This matches the overall simplicity of the language and iswell
documented, but I still find it to be a bit ugly (although ugliness as beauty is in the eye of the beholder).
Overall, I have so far enjoyed the simplicity and consistency of the language, although there are few things I wish were done a bit differently. My eight-year-old son also picked Lua syntax quickly,
which reminded me a lot about my experience with Turbo Pascal decades ago.
相关文章推荐
- Lua: Good, bad, and ugly parts
- SGX: the good, the bad and the downright ugly
- Bad Parts(Appendix B. of JavaScript: The Good Parts)
- Good day and bad day
- Good News And Bad News
- [Forward]Are you a ScrumBut? And if so, is that a good thing or a bad thing?
- 安装gstreamer, gst-plugins-base, gst-plugins-good, gst-plugins-ugly, gst-plugins-bad, gst-ffmpeg的依赖
- (zz)Good and Bad PHP Code
- 10 Rules of Good and Bad Studying By Barbara Oakley
- JavaScript Patterns and Good Parts
- Flash 10 Security Changes Good and Bad, Mostly Good (Full Screen Input, RTMFP, Clipboard, Local Save and Load)(转)
- lightoj1051 Good and Bad (dp)
- 2203: Good and Bad
- Good judgment comes from experience, and experience comes from bad judgment
- Good and Bad PHP Code
- good and bad
- Building Web Parts for Microsoft SharePoint Products and Technologies
- Developing SharePoint WebParts using User Controls and Web Applications
- Why SHRINKFILE is a very bad thing, and what to do about it.
- Personalizing Using Web Parts->Personalization and Web Parts