One problem with functional programming in C is the lack of
automatic dynamic memory management. In a functional
language I can write,
g( print( f( cons( 1, 2 ))))
where »cons« creates a dotted pair
.---.---.
| 1 | 2 |
'---'---'
. In C, however, I have to start thinking:
»cons« uses malloc ... What if malloc, and thereby »cons«,
returns 0, while »f« expects a dotted pair? And when and how
exactly is the memory freed later? Above, it might be suited
when some of the functions frees the pair, but what if such
a function is being called in another context, where freeing
its argument would not be appropriate, such as when it is
being called with a dotted pair in automatic memory instead
of dynamic memory at another place?
Using C with a GC (there are GCs for C) might help in this
regard. But then it's not C C anymore, but GC C!
I suspect that interpreted languages written C which are written in strictly
conforming ISO C (or strictly conforming ISO C plus only library extensions)
are few.
Language projects use C as a high level assembly language.
Their test cases are written in the language that is being processed, and if
those pass on all supported platforms, nobody cares too much about undefined
behavior in the underlying C. They have a working binary.
As far as GC goes, C is fairly unfriendly toward garbage collectors,
in sneaky ways---not just the obvious ways.
Using your functional library for C (FCC?) suppose you do this:
var = NIL; /* suitably defined NIL constant */
your hope here is that the last reference to an object is held
in var, and is being obliterated, so the object becomes garbage.
Alas, the optimizing C compiler performs flow analysis and realizes, "hey,
var has no next use at any node reachable from the assignment in
the flow graph, in the flow graph; it is a dead assignment!".
Poof, the assignment optimized away, no nowhere to be seen in the machine code.
And so, you have a spurious retention bug: some memory or register still holds
on to the object, preventing GC.
The next line of code could be big_long_computation(); and so the object
is held all across that.
Perhaps volatile will solve the problem (but will you stick volatile
everywhere? And there go useful optimizations!)
Or, you can attack it on a case by case basis:
variable = NIL; /* suitably defined NIL constant */
dummy_external_function(&variable); /* does nothing */
Properly, precisely implemented languages that have garbage collection have
compilers which are aware of garbage collection.