
Functional programming in C - rudenoise
http://rcrowley.org/2008/03/03/functional-programming-in-c
======
demallien
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!

------
ajross
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.

------
akkartik
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.

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

~~~
Davertron
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.

~~~
wlievens
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.

~~~
Davertron
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.

------
DrJokepu
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.

~~~
ramchip
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.

~~~
loup-vaillant
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.

~~~
ramchip
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.)

------
pan69
<http://www.newty.de/fpt/index.html>

------
wendroid
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....](http://plan9.bell-
labs.com/sources/plan9/sys/src/cmd/nntpfs.c)

    
    
        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!

~~~
cturner
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)

~~~
wendroid
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 :>

