title | tagline |
---|---|
philosophy |
problem. solution |
Luapower is a custom selection of Lua and C libraries chosen for speed, portability, API stability and a free license. On top of that, a growing collection of ffi bindings and Lua modules, all packaged to minimize the friction in putting together LuaJIT environment for general-purpose programming.
On this page I will try to make the case for luapower based on its main characteristics.
Being easy to use is one of the main goals of luapower. This means leveraging technologies that we already know and use instead of creating new ones. It means providing the lowest-tech method of achieving any task, and providing a more powerful way only as an alternative.
The entire luapower module consumption workflow is based on downloading zip archives from the web and unpacking them over the same directory. The "hi-tech" alternative is git-cloning them using a simple git wrapper script.
The module production workflow is based on using git and some naming conventions. The package database is generated by a Lua script; package information is mostly inferred from the naming conventions rather than explicitly declared. This allows you to focus on writing code, and not dread the moment when you have to package it all. Your module is already packaged from the moment you start writing it, which also means that you can start sharing it from the beginning.
Doing away with packaging encourages publishing smaller and more focused modules individually, rather than bundling unrelated modules together into kitchen-sink-style libraries only because the effort of creating and maintaining individual tiny packages would be too great.
The work tree can be moved around from system to system, and even between different platforms, and just work.
This means including the binary dependencies that the code was actually tested against, instead of relying on the OS ones, and making sure that Lua loads the local ones over the system ones.
It also means putting together all binaries for all platforms in the same tree (no branches). If space is really a problem (android) then deleting other platform directories just before packaging the app is easier than checking out per-platform "binary" branches for each module and thus requiring multiple work trees.
Finally the work tree also works as deployment tree, thus there's no deployment step, unless you have special needs.
Each Lua module has its own life and death, its own repository, commit history, version history (git tags), issues and forks and pull requests.
This allows luapower to evolve gradually and incrementally, allowing old and obsolete modules to stick around for as long as there are users for them, and yet their presence not bother everyone else.
Additionally, luapower modules from different people can be put together to build an entire app by just copying directories over each other, or by cloning git repositories.
One of the great barriers to getting involved in projects online is the friction that our collaboration tools inflict on us. This goes for anything from correcting a typo on wikipedia to submitting a patch to the Linux kernel. Although far from perfect, Github is one solid way to lower that barrier for code-based projects.
Another way of looking at it is Alan Kay's idea of symmetric authoring and consumption, which says that you have to be able to edit and share back what you consume on the spot. The combination of a code editor and a VCS is the closest we have today to that idea as far as coding goes. It's not much, but it's definitely better than most package managers which copy modules out of the git tree or create junk files on your tree.
Deploying code exclusively via git does away with the dichotomy between module writer and module consumer, opening the way for more collaboration. The consumer can always push back changes. A luapower installation is like a distributed wiki in this sense.
Luapower doesn't use semantic versioning. Each compatibility-breaking change is eagerly signaled with a tag change. This is the only honest choice, and if done without reserve, the tag number will show exactly how unstable an API really is. Note that in any language, and Lua is no exception, just adding an extra argument to a function is a compatibility-breaking change, because you can't assume that your function isn't already called with extra arguments in existing code, arguments which the previous version of your function discarded, but the current version does not. With semantic versioning you have three choices in this case: either a) increment the major version now, b) wait until more changes accumulate to warrant a major version bump, or c) lie, and increment the minor version instead. None of these are particularly good options.
Also note that semver is a technical solution to a social problem.
This may be a hard sell but I stand by it. Directories are evil. Not so
much because of semantics (i.e. hierarchies, which by definition only
present a single perspective of a system),
but because of the tools we use (file browsers, code editors, IDEs, command
line) suck at working with them. You don't have instant search in most of them
(Sublime Text excepted), but instead you have to navigate them (this is
true for GUIs and it's true for the command line as well). Also, one of the
major reasons for directory hierarchies is structuring information for humans
to consume in the learning phase. In the case of a module distribution, the
structure of a package is the same for all packages. Thus the juxtaposition
of files from many modules doesn't make things less organized. If you feel
uneasy about a long flat list of files, try mgit --all make-hardlinks
and inspect the contents of packages from the .mgit
directory.
Philosophy aside, having all the modules in the same directory is the way
require()
works by default, which means there's no need to add a custom
require
loader to find modules.
I should not be forced to compile stuff. Not even in Linux. You should let me play around with the library before I even decide if I want to incorporate it in my project or not. Also, I shouldn't need to have a build toolchain on my deployment targets.
People love build systems. I for one hate them and I think for the most part
that they are unnecessary. For me, the success rate for going from
make
to dll
on the first try has always been pretty lousy.
Building with simple gcc scripts is much more transparent,
which directly affects my ability and motivation to fix problems (which in
turn affects the number of packages built). It compiles much faster too.
To me it boils down to how much indirection there is between problem (the .c and .h files) and solution (the gcc compiler). Sure, you have to remember a few gcc switches, but they are the same switches for any library that you will ever build.
This also allows me to apply a set of decisions consistently across the entire distribution, like, if libgcc should be statically built or not.
When it comes to building binaries, package managers usually strive for flexibility, allowing wide variability in the process and the tools used for compilation. With luapower it's exactly the opposite: there's only one supported way to build binaries for each platform. The good news is that you are much more likely to get binaries on the first try this way.
Package type, version, supported platforms, license, etc., this is information that needs to be aggregated and presented in a way that can be sorted and filtered. Very few module websites bother to show that table, for any language.
Make it as easy as possible to produce, and as comfortable as possible to consume. For production, this means plain-text with good support for tables (invaluable for tech docs of any kind), and a markup format independent of any wiki engine, web framework or hosting provider (incidentally, the git-flavored markdown fails on both the independence aspect and table support). I went with pandoc on this because it has the best table support I've seen, and you can even write custom writers in Lua (and filters in any language that can read and write json). It also supports a variety of markup formats for input, which could come in handy for aggregating 3rd party docs into the website. Which brings me to the consumption part: docs should start with a quick-ref table listing the entire API. Chances are you'll rarely look beyond that table once you get past the initial learning curve for that API.
As much as possible, I try to find libraries that are free as-in non-viral, to avoid the case where one module is imposing licensing restrictions on other modules and on your own apps.
My own code is in Public Domain. There are good reasons for avoiding copyright altogether instead of choosing a permissive license, but they cannot be meaningfully expressed without delving into the history of IP law, politics and philosophy even, so I'll not expand on that here (even though this is the philosophy section). If you feel like opening up that can of worms for yourself, try to research an answer to the following practical question: Who is the copyright holder of the output of preprocessing a copyrighted C header file?