3 ms·
I concur. Fexl can whip combinators around so fast it makes my head spin. But I still like writing C, even if it's only to write a Fexl interpreter. Conseque
by fexl 17y ago
I concur. Fexl can whip combinators around so fast it makes my head spin. But I still like writing C, even if it's only to write a Fexl interpreter.
Consequently the whole purpose of Fexl is to be a thin functional "layer" on top of C. That way as a C programmer I can escape to functions to avoid all the gnarly horrors of memory management and such, and as a Fexl programmer I can escape to C to embrace the hard-edged bits and bytes. That way if you asked me whether I was writing the thing in C or in Fexl, I couldn't really give you a straight answer. ;)
- aaronblohowiak 17y agoOff-Topic: Can you do a fexl vs gambit scheme comparison? I am interested in the interleaving of functional programming and C
- fexl 17y agoSure, I'm reviewing the gambit code a bit. I'm really busy so I'll have to follow up later. Right off the bat I can tell you that Fexl's combinators aim to be written in readable C. I suppose the simplest combinator to show would be "I" (the identity function). In Fexl I have a file named "type_I.c" which says: #include "node.h" #include "type_I.h" static struct node *reduce(struct node *parent) { return parent->RIGHT; } static struct type type = { reduce, clear_pair }; struct node *make_I(void) { return make_pair(&type,0,0); } (Quick note: all node values are reference counted. Cycles are impossible, and unnecessary because of the wonderful Y combinator.) Speaking of Y combinator, here is "type_Y.c": #include "node.h" #include "type_app.h" #include "type_Y.h" static struct node *reduce(struct node *parent) { return make_app(parent->RIGHT,parent); } static struct type type = { reduce, clear_pair }; struct node *make_Y(void) { return make_pair(&type,0,0); } Those are easy. The most gnarly one is the S (fusion) combinator ... which ... oh well I might as well paste it in: #include "node.h" #include "type_app.h" #include "type_S.h" static struct node *reduce(struct node *parent) { if (0 == parent->LEFT->LEFT || 0 == parent->LEFT->LEFT->LEFT) { parent->type = parent->LEFT->type; return parent; } struct node *x = parent->LEFT->LEFT->RIGHT; struct node *y = parent->LEFT->RIGHT; struct node *z = parent->RIGHT; struct node *fun = make_app(x,z); struct node *arg = make_app(y,z); hold(fun); hold(arg); parent->type->clear(parent); parent->LEFT = fun; parent->RIGHT = arg; return fun->type->reduce(parent); } static struct type type = { reduce, clear_pair }; struct node *make_S(void) { return make_pair(&type,0,0); } OK now I'm pushing the limits of long-windedness, but I just wanted to feed you some information right away while I work on some other things.
- fexl 17y agoOh gosh how could I neglect a mention of type_app, the type which represents function application itself? #include "node.h" #include "type_app.h" static struct node *reduce(struct node *parent) { struct node *curr = parent->LEFT; while (curr->type == parent->type) { struct node *self = curr->LEFT; struct node *next = self->type->reduce(curr); if (next != curr) { hold(next); drop(curr); curr = next; } } parent->LEFT = curr; return curr->type->reduce(parent); } static struct type type = { reduce, clear_pair }; struct node *make_app(struct node *f, struct node *x) { return make_pair(&type, f, x); } That "while" loop you see there is essentially the only loop in the entire core of Fexl. (Obviously "plug-ins" can do their own loops.) That loop keeps reducing the parent node as long as it's still of type_app. Then the loop ends and it reduces whatever the parent node has become as a result of the evaluation loop.