Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Functional programming in C (rcrowley.org)
23 points by rudenoise on Jan 6, 2010 | hide | past | favorite | 21 comments


Of course, I can't help thinking of Greenspun's Tenth Rule when I see this sort of thing.

That said, my day job is C programming, and I am of course a heavy user of function pointers (how else can you get anything interesting done in C?). But C's type system does tend to get in the way, so you end up casting all pointers to void*, and things like that, just to shut the compiler up. Some Lisp-style macros would certainly help with this!


This seems to be kinda missing the point. Yes, you can do functional programming in almost anything that supports recursion and function indirection. But the essence of functional programming is not simply using a function pointer for everything. Those "if and switch statements" are perfectly functional concepts. It's the avoidance of side effects that generally distinguishes "functional" idioms from imperative ones.


Functional? If anything this is a poor man's OO, simulating a vtable. Even if it is using higher-order functions, that seems like a more accurate analogy.


It's not functional programming if you don't have nested functions and/or closures, is it?


This might be a bit off topic, but since we're talking about functional languages...

I've done a very small amount of functional programming (I did the first 5 or so problems from project Euler in Haskell, for example, just to see what Haskell was like), and there's one specific thing I was wondering about. First, let me give you some example psuedo-code just to describe the situation:

if function_call(value) == some_value_I_want_or_something ? return function_call(value); else return 0;

Ok, that's not any particular language or anything, but I think it'll serve to get my query across.

So in the above code, I want to do something like check the result of a function call, and then if that value is the one I want, return the result of that function call. Otherwise, return 0 (you could do whatever here, but I just made it simple...).

My question is this: do most functional languages do some sort of optimization to avoid basically calling the function twice, since in a purely functional language, functions shouldn't have side affects, so calling a function multiple times with the same input should yield the same output? In other words, does the runtime/compiler/whatever say "I just ran function_call(value) and got a result, and since I don't allow side affects, anytime from here on out that I see function_call(value), I'm just going to assume the result is the same."?

Maybe it's because I'm very inexperienced with functional languages, but I find that I run into that kind of situation often enough, and I always think "Is this going to be really slow because I'm basically calling this function twice even though the result is going to be the same?".

I'm guessing that this may vary language to language; as I understand it, for example, Lisp is "functional", but not purely so, and doesn't really enforce the whole "no side-effects" thing, so it would have to run the function twice, but I could be mistaken.


Your pseudo-code is a little weird (it recurses infinitely?) but I think that what you're referring to is memoization, or basically caching your function calls' results for equivalent arguments. I don't know if any languages support it out the box, but I do know that any language with first-class functions and closures will allow you to make a simple "wrapper" construct so that you get this feature anyway.


Nah, it doesn't actually recurse infinitely, it just looks weird. Here's an example in c:

http://codepad.org/Xysob1jS

Obviously this is a totally contrived example but you get the idea.


"Functional programming" is one of those terms that's so undefined (and, really, indefinable) that it basically boils down to "I'll define it to mean that what I'm doing fits and what you're doing doesn't".


I think most "functional programmers" will agree on this definition: avoid (side) effects, or at least try to isolate them from the rest of your program.

It doesn't mention functions at all, which can confuse programmers unfamiliar with Lisp, ML, or Haskell. However, when you actually program with such restrictions, your only choice is closures or insanity.


"Most" functional programmers might agree that is a component, but that it is missing many other critical elements of a useful definition. Immutability is a recent one, for instance.

Then you get people like me who observe there really are two "functional"s: There's an old one that simply means functions are first-class objects and usually means you get closures. This is the definition whereby Lisp is the canonical functional language. Then there's the more modern definition that involves immutable variables, no side effects or carefully-isolated side-effects, all of the older definition's closures and such, and possibly a few other things depending on who you talk to. Note by this definition Lisp is no longer "functional".

C being C, it can do the older functional style with some manually-hacked-in closures, at which point you can squint and call it an old-style-functional language. Certainly more so than a Basic with no ability to have a function pointer at all, though lacking closure support. (Note I say "a Basic", I know some can and some can't.) No way is it a new-style functional language, and it never will be. (Modulo http://conal.net/blog/posts/the-c-language-is-purely-functio... , of course.)


Side point: I wasn't talking about programming languages. I was talking about programming styles.

Then by my definition (which I no longer consider universal), programming in a functional style drives you crazy unless you can use functions as first class entities.


There is an interesting macro in the glusterfs package that gives you call/cc, across machines no less.

I think functional programming is anything that tries to avoid side-effects for as long as they can be postponed.

There are languages that put an accent on that but you could program in a functional style in just about any language that supports function calls.


Invoking functional pointers (call eax) is very very slow. If you're using C chances are you're using it because you need the extra performance.


Isn't this essentially what a virtual function call does in C++? It can be slow if the code isn't in cache yet, but otherwise it's not that bad.


This is not about the instruction cache. This is about branch prediction. Calling a function pointer (almost?) guarantees that the branch prediction will fail, unlike a switch statement. Not cool in inner loops.


Are you sure? AFAIK the prediction will usually assume that the called address will be the same as the last one, so in an inner loop (eg. mapping a function over an array), only the first call would take a long time, and then only by 20-25 cycles. If a loop is so tight that 5 or so cycles per iteration matters, there shouldn't be a function call in it in the first place.

(I've googled earlier and someone measured a virtual call in a tight loop to take about twice the time of a direct one. Intel doc says a call is about 5 cycles. That would mean an extra 5 cycles or so for the indirection.)


Practically all modern CPUs have indirect branch prediction. The overhead of a function pointer call is practically zero unless the target of the function pointer is actually completely random, which it rarely is: most of the time it's constant for a long period of time (e.g. an in inner loop) until it's changed.



Lots of the plan9 code is written in a style like this but with enums

A bit tricky to find the best example on my phone while on the bus but here's a flavour, the in-memory file system

http://plan9.bell-labs.com/sources/plan9/sys/src/cmd/ramfs.c

Edit : can anyone see how to add a comment to the article, I wanted to share the above info with him but comments don't seem open to me now I'm at my desk.

The plan9 way uses an interface style, the sort that is made explicit in Go :

from http://plan9.bell-labs.com/sources/plan9/sys/src/cmd/nntpfs....

    Srv nntpsrv = {
    .destroyfid=	fsdestroyfid,
    .attach=	fsattach,
    .clone=	fsclone,
    .walk1=	fswalk1,
    .open=	fsopen,
    .read=	fsread,
    .write=	fswrite,
    .stat=	fsstat,
    };
These file systems all run in user mode btw (as can all the disk based ones). Mycrotiv has even done a kernel with a built in rc shell that you attach your file systems after booting as yourself - a disk failure will not take down your system!


Offtopic. That pipe(3) function I saw in ramfx.c is cool (http://swtch.com/plan9port/man/man3/pipe.html). Bidirectional - unlike *nix. I think life would be easier if it were more straightforward to build applications as chains of little utilities with one-to-one protocols of integer exchange, rather than having to go to socket (more complex, and port management overhead) or file (ugly). I have a things-might-have-been-very-different feeling.

When is Team Plan9 going to produce a distribution that we can get into using as easily as Linux or the BSD kernels? :) (Or would inferno be a better focus? Same issue though - it's not as easy as ubuntu or even small projects like haiku)

Also offtopic - I like doing switch statements in python like this:

    def fn_a(input_tokens): pass

    def fn_b(input_tokens): pass

    user_cmd = raw_input().split()

    # look mum, no hands
    { 'a': fn_a
    , 'b': fn_b
    }[user_cmd[0]](usr_cmd)


The notion you talk of with your integer exchange is called "channels" which are typed pipes. see http://plan9.bell-labs.com/magic/man2html/2/thread for the C library

On Inferno the Limbo language has channels as a type with their own operators

Send an integer down chan <- = 5; and read it out i = <- chan

I don't understand what you mean by "get into as easily". The distribution CD is also a Live CD so you can boot right into it. The kernel code can be understood by one person. There are commentaries such as http://lsub.org/who/nemo/papers.html

It runs in qemu, vmware player, Xen, Lguest

We are thinking of doing a qemu image release but not worked out an auto installation system quite yet (the distribution cd is rebuilt every night with the new commits).

We have irc://irc.freenode.org/#plan9 for unofficial chat (though with the usual caveats)

And a mailing list where you might get your question answered by Rob Pike, Dennis Ritchie, Russ Cox or one of the many knowledgable people involved in or using the project (plan9 runs on IBM's Blue Gene supercomputer for instance and is used at LANL, Sandanista, NASA JPL, MIT).

There's also Russ' usermode projects :

9vx - http://swtch.com/9vx/ a plan9 that runs in the vx32 x86 sandboxing environment

and

plan9port - http://swtch.com/plan9port/ - which is the plan9 user tools ported to Lunix

I'd say that was a quite comprehensive set of "get into" tools :>




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: