Post by Glenn MaynardPost by Mike Palllocal userops = {
PI = { arity = 0, op = math.pi }, -- arity 0 are constants
sin = { arity = 1, op = math.sin },
cos = { arity = 1, op = math.cos },
["|"] = { arity = 2, op = bit.bor },
["<<"] = { arity = 2, op = bit.lshift },
["//"] = { arity = 2, op = function(a, b) return math.floor(a/
b) end },
} -- These should of course better be light functions.
loadstring([[
local a, b = ...
print( PI * sin a + cos b )
print( (a << 11) | (b << 3) | 5 )
print( "Rici buys", 1.38 // 0.16, "apples." )
Intuitively, I don't like this. I don't want to use a language where people
are almost defining their own syntax inside the language. That takes
the evils that operator overloading can cause in C++ (people using operators
for things unrelated to their original cause, like overriding + for "set
union"), and makes it an order of magnitude worse, allowing making whole
new operators.
That's not to say it couldn't be used well, just like operator
overloading
can be used well; it just seems to invite incredible abuse. Practical
languages can't make it impossible to write bad code, of course--
people
will--but this seems at such a level that in order to read anyone else's
code, I'm going to have to learn their own personal sub-language first.
See below.
Post by Glenn Maynard(I don't understand how a parser could support this, since the
language
is being defined by the language being compiled.)
No it's not. The table defining the language, userops above, is not
part of the program being compiled, the string argument to
loadstring. I assume Rici Lake meant to pass userops to loadstring
or something like that. Effectively the compiler (as embodied in
loadstring for example) changes from a function of type "string ->
function" to one having type "string x language -> function" where
"language" could be specified as a table like Rici Lake has done.
On sub-languages:
One paradigm for programming solves problems by creating a new
language in which the expression and solution of the problem in hand
is, in some sense, "nice". This is one of the reasons people use
different languages for different problems. To some extent it's
possible to regard _all_ programming as the creation of a new
language in which the problem solution is expressed.
For example when looking at a random program in C, say the Lua
system, it's not possible to understand the code without
understanding the specialised language that has been created.
Concretely, here's a snippet of code from Lua:
int b = GETARG_B(i);
int nresults = GETARG_C(i) - 1;
if (b != 0) L->top = ra+b; /* else previous instruction set
top */
L->savedpc = pc;
Now, this is fairly plain C, but knowing C doesn't really help you
understand what's going on. You have to understand the language of
"GETARG_B" and "L->top" and such like. A language has been created
with the "C language programming system".
Where programming systems (languages) differ is in how much
flexibility they offer you in defining your own language. My
assertion is that you'll be defining your own domain-specific
language anyway, regardless of how much support you get from the
programming system you are using. Languages like Java offer almost
no flexibility. Your language pretty much must consist of objects
and method calls, so it's all in the names and typical usage idioms.
Languages like C offer quite a bit more (because of the horrible
macro system); you can to some extent define you're own syntax for
some things. Languages like Prolog or Lisp offer a huge range of
flexibility due to the way you can get at the internals of the parser
("reader").
Lua seems to occupy a sort of middle ground somewhere between Java
and Lisp. You can make languages that look a bit declaration and
configuration-like by using syntax like:
base { pos={3, -7}, health=50 }
you can make fairly ordinary "just a pile of functions" languages:
base[i] = newBase({3, -7}, 50)
And you can also make some quite funky languages by messing around
with metamethods and the built-in operators.
Observe that load takes a function that generates input, so one could
regard this as a sort of completely general front-end to the Lua
"parser" capable of transforming any language into Lua. That kind of
thinking would place Lua near the Lisp end of the spectrum, but it's
not really because of the general difficultly of creating typical
transformations.
Perhaps it boils down to: to what extent do you trust your developers
(people that write code that you have to look at, say) to design
sensible languages? I observe that what C++ makes easy, operator
overloading, seems to make it easy for people to create languages
that are not intuitively understood, and are therefore "bad".
I find all this a bit ironic since you Glenn have created your own
sub-language with stuff like: http://stepmania.cvs.sourceforge.net/
stepmania/stepmania/Themes/default/metrics.ini?
revision=1.1344&view=markup . In order to "understand" that document
I have to understand the sub-language of "[ScreenUnlockStatus]" and
"# blah blah blah" and so on. I'm not saying that's difficult in
this case, I'm just saying that you create sub-languages of your own.
Personally I really like the fact that compilation in Lua is
currently a function of type "string -> function". The result of
compilation does not depend on the compilation environment. OTOH I
also think much of the recent "syntax modification" discussions and
developments, such as Silverstone's token-processing patch and Lake's
example above, are very interesting.
drj