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.

Reply via email to