From: John Thingstad on
The Thu, 12 Nov 2009 18:40:03 +0530, Madhu wrote:

> | I disagree. This article is spot on in adressing the issues which |
> cause side effects. It is more sad to peolple like youself so |
> unappriciative.
>

> However the point is CL's macro system is not hygienic and CL is not
> about restricting side effects.

No, the interesting point is to know what causes side-effects and how you
can get around them.
Less interesting is that you can implement a unhygienic macro facility in
a hygienic one.

> The fact that the CL macro system is
> powerful enough to implement a hygienic subset is not an especially
> interesting result, in the same sense that you can always use a more
> powerful system to build a more restricted less expressive system.

But Hygienic macros are not more restrictive. It is more work to
introduce side effects but you can certainly do so.
In fact the paper describes how this is done.

All it really comes down to is: Do you spend more time writing code to
avoid side effects in CL than you would writing code to produce them in
Scheme? Not spending a lot of time writing macros in Scheme I can't
answer that.

--
John Thingstad
From: Ron Garret on
In article <2009111921551816807-tfb(a)cleycom>,
Tim Bradshaw <tfb(a)cley.com> wrote:

> On 2009-11-19 05:26:34 +0000, Kenneth Tilton <kentilton(a)gmail.com> said:
>
> > No, this guy is a Java fruitcake who thinks he has black helicopters he
> > can send after you. Nuff said?
>
> Given up Java, now using Python as it's more fashionable. Ruby is
> next, I think. The helicopters are a real problem - used to be funded
> by those nice bank people but they're all civil servants now, and it
> appears that the tfb helicopter co was not too big to fail. We can
> probably still afford to have Madhu abducted...
>
> Now, seriously. I have a program in mind which:
> * will need to parse XML config files (realistically: I'd use sexps if
> I have the choice but the target market will not accept that.
> * needs to run on a wide variety of Unix/Linux platforms (recompilation
> is fine) including very old versions of the OS
> * needs to be pretty intimate with POSIX
> * probably will ship source but do not want GPL contagion
>
> i was thinking of doing this in Java (and shipping a JRE it works on,
> the way everyone else does), but there seems to be no serious posix
> bindings for Java at the level I need (things like working out the
> current UIDs/GIDs of the process, calling setuid &c). So I'm thinking,
> realistically, Perl (and ship a perl runtime with it).

Noooooooooo!!! Perl is a freakin' nightmare. (That's one thing Erik
and I definitely agreed on.)

>
> Looks to me like the only Lisp option would be CLISP.

Try CCL (Clozure Common Lisp). I believe you will find it meets all
your needs.

rg
From: Kaz Kylheku on
On 2009-11-19, Tim Bradshaw <tfb(a)cley.com> wrote:
> Now, seriously. I have a program in mind which:
> * will need to parse XML config files (realistically: I'd use sexps if
> I have the choice but the target market will not accept that.
> * needs to run on a wide variety of Unix/Linux platforms (recompilation
> is fine) including very old versions of the OS
> * needs to be pretty intimate with POSIX
> * probably will ship source but do not want GPL contagion

You could use C.

Seriously. Just base it on the internals of my new txr program.

http://savannah.nongnu.org/projects/txr

Running on very old versions should be fine. You do need the <wchar.h> header
from C95. Other than that, just C89 and POSIX.1 and POSIX.2 is needed.

Portabilty could be a bit dodgy due to the GC. Works on x86_64 and i386.

Intimate with POSIX: check.

Ship source: New BSD license, no GPL: check.

You've got:

- everything written in C, using C functions, with C local variables
on the real stack.
- dynamic typing and garbage collection.
- gc scans the stack, so no special protocol to ``hold'' local variables
to protect them from gc.
- simple mechanism to register globals into the root set.
- hashes with weak keys and values (poorly tested, but there)
- all text in wide characters, and UTF-8 streams.
- string streams, and a simple formatter.
- simple regular expression engine with an s-exp input language,
which handles Unicode character classes.
- symbols
- nonlocal transfers and exceptions
- very basic library, but easy to extend.

It's easy to add your custom objects whose guts are written in C
and play along with the garbage collected world.

A flavor of the code:

/* exhibit 0: formatting

``nao'' is a special symbol used for terminating variable argument
lists; helps variadic functions detect errors. nao is a distinct
bit pattern from any object, including nil.

If format sees nao when extracting an argument, it throws an exception.
If format does not see nao as the next argument after processing
the format string, ditto.

So format is a lot more safe that C printf.

lit() is a macro. It efficiently turns a string literal into a first-class
object, without consing any memory, so the format function's API does not
use C strings.

L is added to the literals, making them wide strings (wchar_t chars). */

std_output performs UTF-8 encoding, outputting bytes. */


{
format(std_output, lit("Hello, ~a!\n"), lit("world!"), nao);
...
}


/* exhibit 1: lispy code

Note: this is not at all a normal Lisp-like eval and not in the basic
library; it's a concept specific to the txr application.
No evaluator is provided in the library, since code is
written in C and compiled.

identity_f is a "pre-cooked" function object for identity,
so we don't have to cons it up when we need it.

regexp means regex_p: ``is a regex'' :) */

obj_t *eval_form(obj_t *form, obj_t *bindings)
{
if (!form)
return cons(t, form);
else if (symbolp(form))
return assoc(bindings, form);
else if (consp(form)) {
if (car(form) == quasi) {
return cons(t, cat_str(subst_vars(rest(form), bindings), nil));
} else if (regexp(car(form))) {
return cons(t, form);
} else {
obj_t *subforms = mapcar(bind2other(func_n2(eval_form), bindings), form);

if (all_satisfy(subforms, identity_f, nil))
return cons(t, mapcar(func_n1(cdr), subforms));
return nil;
}
} if (stringp(form)) {
return cons(t, form);
}

return cons(t, form);
}


/* exhibit 2: posixy
ooops, I see a bug here: the handle == 0 case should throw a file_err;
someone is trying to read a directory stream with no handle. */

static obj_t *dir_get_line(obj_t *stream)
{
DIR *handle = (DIR *) stream->co.handle;

if (handle == 0) {
return nil;
} else {
for (;;) {
struct dirent *e = readdir(handle);
if (!e)
return nil;
if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, ".."))
continue;
return string_utf8(e->d_name);
}
}
}

/* exhibit 3: snippet illustrating named blocks use */


{
uw_block_begin(block_symbol, result_variable);

/*

code dynamically in here uses
uw_block_return(<symbol>, <value>) to
dynamically exit from this block.

*/

uw_block_end;

/* result_variable holds block return value */
}

/* exhibit 4: long snippet showing flavor of combined exception
catching and unwind-protect.

NOTE: this code is /interpreting/ a language that provides
try/catch/finally, that's why there are references to these
names. Don't get confused.

The items of interest here are the uw_catch_begin,
uw_catch, uw_do_unwind and uw_unwind macros.

uw_catch_begin/end wrap the whole protected block.
catch_syms is a list of exception symbol types, subtypes of which
are caught; exsym is the name of a variable that is defined
by the macro which will hold the actual type of the exception caught,
exvals is the name to use for the variable that holds the exceptino
value.

The uw_catch (..., ...) catch block is invoked when a matching
exceptino is caught.

the uw_unwind block is run always whenever a non-local
jump passes through.

The uw_do_unwind; macro is a little hack. In the normal return
case from the protected code, or in the case of catching,
it provides the glue to run the unwind cleanup code also
in the uw_unwind block. But not only a hack, this gives you a choice to
always do the cleanup, or skip it. */


{
uw_block_begin(nil, result);
uw_catch_begin(catch_syms, exsym, exvals);

{
result = match_files(try_clause, files, bindings,
data, num(data_lineno));
uw_do_unwind;
}

uw_catch(exsym, exvals) {
{
obj_t *iter;

for (iter = catch_fin; iter; iter = cdr(iter)) {
obj_t *clause = car(iter);
obj_t *type = first(second(clause));
obj_t *params = second(second(clause));
obj_t *body = third(clause);
obj_t *vals = if3(listp(exvals),
exvals,
cons(cons(t, exvals), nil));

if (first(clause) == catch) {
if (uw_exception_subtype_p(exsym, type)) {
obj_t *all_bind = t;
obj_t *piter, *viter;

for (piter = params, viter = vals;
piter && viter;
piter = cdr(piter), viter = cdr(viter))
{
obj_t *param = car(piter);
obj_t *val = car(viter);

if (val) {
bindings = dest_bind(bindings, param, cdr(val));

if (bindings == t) {
all_bind = nil;
break;
}
}
}

if (all_bind) {
cons_bind (new_bindings, success,
match_files(body, files, bindings,
data, num(data_lineno)));
if (success) {
bindings = new_bindings;
result = t; /* catch succeeded, so try succeeds */
if (consp(success)) {
data = car(success);
data_lineno = c_num(cdr(success));
} else {
data = nil;
}
}
}
break;
}
} else if (car(clause) == finally) {
finally_clause = body;
}
}
}
uw_do_unwind;
}

uw_unwind {
obj_t *iter;

/* result may be t, from catch above. */
if (consp(result)) {
/* We process it before finally, as part of the unwinding, so
finally can accumulate more bindings over top of any bindings
produced by the main clause. */
cons_bind (new_bindings, success, result);
if (consp(success)) {
data = car(success);
data_lineno = c_num(cdr(success));
} else {
data = nil;
}
bindings = new_bindings;
}

if (!finally_clause) {
for (iter = catch_fin; iter; iter = cdr(iter)) {
obj_t *clause = car(iter);
if (first(clause) == finally) {
finally_clause = third(clause);
break;
}
}
}

if (finally_clause) {
cons_bind (new_bindings, success,
match_files(finally_clause, files, bindings,
data, num(data_lineno)));
if (success) {
bindings = new_bindings;
result = t; /* finally succeeds, so try block succeeds */
if (consp(success)) {
data = car(success);
data_lineno = c_num(cdr(success));
} else {
data = nil;
}
}
}
}

uw_catch_end;
uw_block_end;

if (!result)
return nil;

if ((spec = rest(spec)) == nil)
break;

goto repeat_spec_same_data;
}
From: mdj on
On Nov 20, 7:55 am, Tim Bradshaw <t...(a)cley.com> wrote:

> Now, seriously.  I have a program in mind which:
> * will need to parse XML config files (realistically: I'd use sexps if
> I have the choice but the target market will not accept that.
> * needs to run on a wide variety of Unix/Linux platforms (recompilation
> is fine) including very old versions of the OS
> * needs to be pretty intimate with POSIX
> * probably will ship source but do not want GPL contagion

That's not enough information to base a recommendation on, but I would
say if you're going to have to ship a runtime, CL would be ideal.

Other than that, you mentioned Python. Most of your target platforms
will already have an implementation installed so as long as you target
an older edition (say 2.4) you'll be fine.

Unless of course you need to be 'intimate' with POSIX threads, but
like I said not enough information.

Matt
From: Ron Garret on
In article <20091119165505.75(a)gmail.com>,
Kaz Kylheku <kkylheku(a)gmail.com> wrote:

> On 2009-11-19, Tim Bradshaw <tfb(a)cley.com> wrote:
> > Now, seriously. I have a program in mind which:
> > * will need to parse XML config files (realistically: I'd use sexps if
> > I have the choice but the target market will not accept that.
> > * needs to run on a wide variety of Unix/Linux platforms (recompilation
> > is fine) including very old versions of the OS
> > * needs to be pretty intimate with POSIX
> > * probably will ship source but do not want GPL contagion
>
> You could use C.
>
> Seriously. Just base it on the internals of my new txr program.

Greenspun's tenth in all its horrible glory.

rg