Hi Mike! I'm not a language theorist either... I don't want to be a jerk and just post a link, but to clarify my thinking I also looked at the wikipedia articles:
http://en.wikipedia.org/wiki/Declarative_programming http://en.wikipedia.org/wiki/Imperative_programming Makefiles (and Tupfiles too) are something of a hybrid between declarative and imperative, because while you have rules that can be specified in any order, you also have a layer of imperative code mixed in that is setting (and mutating) variables and macros. To try to clarify what (I suppose) makes imperative code harder to parse and automatically upgrade: In a declarative language, to know what a rule does, you only need to look up what symbols/variables it references, and what they depend on, recursively. Since a symbol can only be defined once (modulo scoping rules or modules that are effectively mangling names so that they are unique), once you've found all the symbols you need, you can stop parsing the rest of the code. In a procedural language, to know what to do with a line of code you need to know the control flow the program will take to get up to that time, and find all places where the symbol was set and what order they're set in the control flow, and then figure out where setting of the symbols ~they depend on appear in the control flow, and so-on, recursively. So, for the example you give, the difference isn't in those two lines, it's in all the build logic people will implement in unrestricted lua in the surrounding code for the lua example. And it is that logic that I think will be hard to upgrade to a better specification language if someone comes up with one. Is it possible to disallow re-binding of variables in lua? One thing that is clear is that in almost build specifications (including all of mine), people are frequently mutating lists of strings that get passed into compilers, and lists of dependencies. So if you remove the ability to do stuff like: FOO=-IA (specify rule 1 using A) FOO+=-IB (specify rule 2 using B) FOO+=-IC (specify rule 3 using A and C, and also has B as cruft because the programmer was lazy) You need to have a very lightweight alternative. Maybe something like: FOO=-IA specify rule 1 needing A ( FOO+=-IB (specify rule 2 using B) ) ( FOO+=-IC (Specify rule 3 using A and C, with no B cruft since the programmer wasn't allowed to be lazy) ) So in the second case, while in vim you can't jump around on FOO and only see stuff that matters, it is at least easy for a parser to know that the FOO in the scope where rule 2 is defined is irrelevant to rule 3. Of course the same example could be done with indentation for scoping: FOO=-IA (specify rule 1 needing A) FOO+=-IB (specify rule 2 using B) FOO+=-IC (Specify rule 3 using A and C, with no B cruft since the programmer wasn't allowed to be lazy) (Note, this is not exactly how python does it; in python blocks also have a colon before them, allowing you to have empty lines within the indented block.) And of course the programmer always has the option of doing name mangling manually... Side rant about lua: I use lua daily (since the orocos developers also decided to use lua as their next specification language), and it is one of the ~least safe languages around. It gives you even more rope to hang yourself with than python, what with the whole table lookup failures returning nil instead of throwing an error, and having all variable accesses be accesses into a table for the variables in your scope. http://www.luafaq.org/#T1.6 In lua your namespace is just a table called _G that you can muck around with like any other namespace. You can programmatically add symbols to your namespace without them even appearing in your code. Manipulating your namespace directly isn't even some obscure, discouraged thing like it is in Python; it's part of the (3? 4? 5?) idiomatic lua ways for defining and importing symbols from a library. Under deadline pressure I ~have used namespace hacks to avoid retyping lists of symbols, as well as abusing the global namespace and using "dofile" even more often than "require", but I'm not proud of it and consider it technical debt. I've never done hard drugs and would never seek them out, but if I had a line of coke sitting on my monitor all the time during a deadline push... Cheers, Andrew On Sat, Jul 26, 2014 at 5:01 AM, Mike Shal <[email protected]> wrote: > On Sat, Jun 21, 2014 at 10:46 AM, Andrew Wagner <[email protected]> > wrote: > > Hello tup-using community! >> >> I would like to voice a concern that I have about the direction the open >> source community is taking regarding build systems. >> >> Almost every single new build system that is gaining any traction is >> built using an imperative, rather than declarative specification language. >> Consider: >> >> Build systems with DECLARATIVE specification languages: >> make >> shake (?) >> makepp (?) >> gyp >> ninja >> tup (w/Tupfile parser) >> >> Build systems with IMPERATIVE Build Systems: >> CMake >> Waf >> SCons >> tup (w/ lua parser) >> >> By their nature, declarative languages are way easier to parse >> recursively, and export in a new modified syntax (or export to an >> imperative language). This is not just theoretical; makepp and shake both >> parse makefiles. >> >> With a procedural specification language, it is easy to hammer out a huge >> pile of (often metaprogrammed) code that is difficult to read, and damn >> near impossible to parse automatically. >> >> >> > Hmm, sounds interesting. Can you clarify what exactly you mean by > declarative and imperative? Or why one is good for build descriptions and > the other is bad? I have seen both ugly and impossible to follow SCons > files as well as ugly and impossible to follow Makefiles, so at first > glance neither would seem to be a win. Plus in my mind, there isn't much > difference between: > > : foreach *.c |> gcc -c %f -o %o |> %B.o > > and: > > tup.foreach_rule({'*.c'}, 'gcc -c %f -o %o', '%B.o') > > Why is one good and the other a dark road we shouldn't go down? > > I imagine it's not too surprising given the state of things, but I'm not a > very good student of what makes a for a good language/configuration design. > To me, the much more interesting & important aspect is what happens when > the rule is parsed. Ie: how to efficiently add the new commands described > by the rules to the DAG, how to remove stale commands, how to handle > conflicts between the commands, or circular dependencies, etc. I don't > think many (any?) other build systems take this approach to parsing - > usually they just parse the whole DAG every time, which is obviously dumb. > > -Mike > > -- > -- > tup-users mailing list > email: [email protected] > unsubscribe: [email protected] > options: http://groups.google.com/group/tup-users?hl=en > --- > You received this message because you are subscribed to the Google Groups > "tup-users" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > For more options, visit https://groups.google.com/d/optout. > -- -- tup-users mailing list email: [email protected] unsubscribe: [email protected] options: http://groups.google.com/group/tup-users?hl=en --- You received this message because you are subscribed to the Google Groups "tup-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
