
Functional C Programming: Higher order functions, closures, lifted types - cioc
http://blog.charlescary.com/?p=95
======
shared4you
Doing functional programming in C reminds me of Greenspun's tenth rule: Any
sufficiently complicated C or Fortran program contains an ad hoc, informally-
specified, bug-ridden, slow implementation of half of Common Lisp

~~~
BadDesign
Great point! :-) I lol'd

------
iskander
I'm not really sure if this counts a functional programming _in_ C but it does
serve as a decent basis for teaching the implementation of functional
languages _on top_ of C. As always, the distinction between a library and a
whole new language is never quite clear...

------
dkhenry
When reading this title my default reaction was "Oh God Why" I am happy to see
the author agrees with me. As an intelectual excersize I think this is great
showing that yes C is very very flexable ( You can do the same stuff with C++
and it will look nicer BTW[0]) but please don't do this, Its messy and hard to
trace.

[0:
[http://www.boost.org/doc/libs/1_49_0/libs/range/doc/html/ran...](http://www.boost.org/doc/libs/1_49_0/libs/range/doc/html/range/reference/adaptors/introduction.html)

~~~
ArchD
Yes, and very type-unsafe. You could do all this using C++ templates in a
type-safe way.

------
mappu
Here's my variation on bind() - generating executable code on the heap allows
you to treat the bound function as a raw ordinary function pointer;
<https://gist.github.com/3154939>

~~~
RodgerTheGreat
I've used a very similar trick for adding partial function application to
Forth:
[https://github.com/JohnEarnest/Mako/blob/master/lib/Algorith...](https://github.com/JohnEarnest/Mako/blob/master/lib/Algorithms/Curry.fs)

If I understand correctly, though, your implementation leaks memory. In my
case I use a GC to free the thunks when they are no longer accessible.

~~~
mappu
Indeed, in typical C fashion all the created stubs must be freed manually.

Adding a GC is a separate task and several exist for C. Though if you were
using C++, you could put together some RAII sugar or smart pointers around it
all, but in that case you have std::bind1st and functors available anyway
(although this is still the only way to meet the raw-pointer use case).

------
tantalor
I wasn't sure what a "lifted type" was so I looked it up,

    
    
      A type is lifted iff it has bottom as an element. Closures always
      have lifted types: i.e. any let-bound identifier in Core must have
      a lifted type. Operationally, a lifted object is one that can be
      entered. Only lifted types may be unified with a type variable.
    
    

[http://hackage.haskell.org/trac/ghc/wiki/Commentary/Compiler...](http://hackage.haskell.org/trac/ghc/wiki/Commentary/Compiler/TypeType)

~~~
Snoptic
... Which is an arcane way of saying "pointer / reference"; i.e., not a
primitive or C-style struct.

------
jerf
Actually, the "closure" pattern shows up in real code; for instance I saw it a
lot in libpurple. It's one of those things that's hard to do any sort of
large-scale engineering without. As long as you can get the memory management
right on it, it's great. (And I wouldn't say that's any more particularly
difficult than a number of other useful C patterns.)

~~~
andrewcooke
if anyone is curious -
[http://hg.pidgin.im/pidgin/main/file/eb0eb02fce71/libpurple/...](http://hg.pidgin.im/pidgin/main/file/eb0eb02fce71/libpurple/value.c)

tbh that's the first time i've seen something quite like that. i am not sure
this is so common a pattern (usually, imho, you can be more explicit and have
a struct that associates a known callback type with named, typed values).

~~~
jerf
No, I wouldn't expect it is "common", but it definitely shows up. libpurple
doesn't use it because it does its own thing, but glib has library support for
it: [http://developer.gnome.org/gobject/stable/chapter-
signal.htm...](http://developer.gnome.org/gobject/stable/chapter-
signal.html#closure)

Although a straight-up Google search for "g_closure_invoke" is amusing. I did
add the "as long as you can get the memory management right on it" for a
reason... page after page after page of "segmentation fault in
g_closure_invoke" stack traces....

~~~
asveikau
I'm not sure why "getting the memory management right" is the hard part, or
why it's any more or less hard than general C programming practice. For
example a closure object with a reference count that releases references on
its contained objects when destroyed seems like a perfectly workable pattern.
(I'd be surprised if that's not how the glib closures are supposed to work,
knowing that glib like many general-purpose C libraries is pretty big on
reference counting.) NSInvocation in objc comes to mind, where IIRC the
closure-like object can hold a reference to its arguments, though it's been
years since I've used that...

~~~
pi18n
I think if you have objects and a reference-counting GC already this problem
is pretty easy, but I assume the C programmers don't want that for performance
or portability or some other reason.

NSInvocation has a signature of the variable types of the method it
represents, so it can retain the object ones without trying to retain plain
integers.

Modern Obj-C uses blocks to make closures, which the compiler changes into
structs holding all the variables referenced. It knows the types of these as
well, so it writes code to retain any object types and releases them as it is
released. They haven't got a cycle collector in the runtime, so programmers
still make mistakes by accidentally making strong cyclic references.

Assuming the reference-counting GC, it's not hard, but you can still make
mistakes.

~~~
asveikau
> I think if you have objects and a reference-counting GC already this problem
> is pretty easy, but I assume the C programmers don't want that for
> performance or portability or some other reason.

Uh. Every large C code base I've worked with has had this in some form,
usually fairly pervasively. I've mentioned glib as a library that exercises
this heavily. In the Windows world this would also include COM. Kernel mode
stuff on various platforms tends to do a lot of refcounting as well.

Note that "reference counted object" is just a fancy word for "struct with an
integer" - I feel like your phrases like "if you have objects" and "GC"
strikes me as confusing this with other features in other languages, a sign
that we're not entirely talking about the same thing.

Edit after more time spent away from it: I guess to restate the original
comment, what I was trying to say is that to an experienced C programmer
closures don't make anything "more hard" - it's just the same memory
allocation strategies that you'll be dealing with anyway, and there are
already well understood solutions (such as having one of your struct fields be
a reference count).

~~~
pi18n
I understand. I think you are right that it wouldn't make things more hard, I
misunderstood that as implying that people wouldn't make memory mistakes under
those conditions. So I was writing that one would require a GC to eliminate
those kinds of mistakes.

I agree with you, reference counting is not something that would confuse an
experienced programmer, but you do have to be aware of what's happening with
your objects.

------
ilaksh
This illustrates the fundamental problems of C very well, which are the
requirements to be very specific about types and manually manage memory.

Of course those things aren't a problem in cases where you need to do those
things, but that isn't the case for most applications.

------
dschiptsov
Why not to read the code of various advanced CL and Scheme implementations?)
MIT Scheme and SCM and CMUCL are good candidates.)

------
yarou
I saw a similar post a few months ago. You can program functionally (not
really) in C. There are certain theoretical assumptions that go along with it.
But at its heart, C is still an imperative language. I'm not sure what the
point is in making C good at doing things "functionally".

------
ExpiredLink
Of course, this skips negligible parts of C programming like memory
management.

~~~
ScottBurson
Oh, just throw the Boehm conservative collector on it and be done with it.

~~~
ExpiredLink
done with C, yep

------
martinced
TFA says that was done answering an Haskell programmer.

Probably that an Haskell programmer values strong static typing just as much
as functional programming.

I someone think that turning C from a weakly static type system to a strongly
static one would prove quite more difficult... (but my C days are long gone so
don't quote me on that).

------
wildranter
1) imperative programming 2) functional programming 3) OOP 4) etc programming

They're jus programming paradigms. Take what you need and get the job done.
Anything else is just a buzzword.

~~~
derleth
This little homily adds nothing. That's why it's getting downvoted.

(It's also wrong, or over-simplified, in that all programming doesn't really
fit into those little boxes.)

~~~
wildranter
Those little boxes are all boxes there are (except for prolog).

BTW, downvotes are as meaningless as i--.

~~~
icebraining
_Those little boxes are all boxes there are (except for prolog)._

No, they aren't, and a simple web search would've revealed others, such as
Dataflow[1].

[1]: <https://en.wikipedia.org/wiki/Dataflow_programming>

