
Functional programming in C (2013) - vasili111
https://lucabolognese.wordpress.com/2013/01/04/functional-programming-in-c/
======
michaelmior
This is an interesting idea. For one of the first year programming courses
here at UWaterloo, we start by teaching students Racket. To ease them into C,
we use a subset we refer to as "functional C."[0] This is really just C with a
few restrictions. The main ones are that all "variables" and parameters must
be const and all branches of an if statement must return a value.

While these restrictions may seem silly to the HN crowd, it leads to an
interesting way of thinking about solving problems.

[0]
[https://www.student.cs.uwaterloo.ca/~cs136/handouts/03-funct...](https://www.student.cs.uwaterloo.ca/~cs136/handouts/03-functional-
c-post.pdf)

~~~
e12e
> and all branches of an if statement must return a value.

Is indeed a bit strange. I guess I can see the reasoning, though.

"const by default" doesn't seem all that unreasonable to me - isn't that one
of the major driving forces we're seeing in "new" languages - that based on
years of experience, "accidental" mutability should be avoided -- mutability
should _not_ be the default?

How does this subsetting of C affect the error messages the students are
(most) likely to see? Does -Wall typically catch treating a const as mutable?
(I assume so, I'm just curious about this, as I'll soon be teaching a
beginning programming course - and a sub-set of C might be an interesting
option).

~~~
michaelmior
We are pretty liberal with the warnings. -Wall will catch trying to mutate
const variables. We also use address sanitizer[0] with clang which helps a lot
with common memory errors. All this makes the code run (relatively) quite slow
but since anything they're testing should complete in a few seconds
regardless, it's not a problem for us in practice.

[0]
[http://clang.llvm.org/docs/AddressSanitizer.html](http://clang.llvm.org/docs/AddressSanitizer.html)

------
pornel
I'll be that guy who mentions Rust.

It's linkable with C. It has first-class support for discriminated unions.
Very powerful automatic cleanup. Library of minimal-overhead common data
structures. Plus immutability, and a few more things heavily inspired by
functional languages.

So instead of a bit odd, not-quite-portable C macros I wholeheartedly
recommend using a bit odd, not-quite-portable Rust instead.

~~~
kartD
Seconded, I think Rust is an excellent choice for writing new code in large C
code bases. The compiler catches so many memory bugs, I think it's a worth
using. I think it'll help more people contribute to open source as well.
They're less likely to add new bugs or security risks.

------
vardump
Ugly and unportable code. I can't see practically any benefit.

    
    
      #ifdef __GNUC__
    
          value       =   c->kind == Volvo    ? ({
                                                  struct Volvo* it = &c->Volvo;
                                                  itoa(it->x, temp, 10);
                                                })
                        : c->kind == Ferrari  ?   (void)c->Ferrari.model, c->Ferrari.brand
                        : c->kind == Fiat     ?   c->Fiat.model
                                              :   union_fail("Not a valid car type");
    
          testCar(c, value);
    
      #endif // __GNUC__
      }

~~~
bluetomcat
Agreed, "functional" code in C is when you write pure functions with no side
effects and nest them like `f(g(h(x)))`. Everything else is an abomination.

~~~
theprotocol
I love this style. I accidentally "discovered" that pure functions were good
through trial and error in my younger days, before I'd looked into functional
programming. However, while I'm not necessarily a gigantic fan of functional
languages, I think functional principles should be taught to everyone.

------
0xmohit
Also worthwhile mentioning is functionalC [0].

The blog mentioned on the Github page is down but the relevant URLs can be
accessed from the wayback machine at [1] and [2].

GCC also supports nested functions [3] (as a GNU C extension).

Unrelated, but [4] specifies a pure C implementation of Go channels and
libmill [5] is a library that introduces Go-style concurrency to C.

[0] [https://github.com/cioc/functionalC](https://github.com/cioc/functionalC)

[1]
[https://web.archive.org/web/20160406001106/http://blog.charl...](https://web.archive.org/web/20160406001106/http://blog.charlescary.com/?p=95)

[2]
[https://web.archive.org/web/20160304120857/http://blog.charl...](https://web.archive.org/web/20160304120857/http://blog.charlescary.com/?p=150)

[3] [http://gcc.gnu.org/onlinedocs/gcc/Nested-
Functions.html](http://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html)

[4] [https://github.com/tylertreat/chan](https://github.com/tylertreat/chan)

[5] [https://github.com/sustrik/libmill](https://github.com/sustrik/libmill)

~~~
rabz
I'll piggyback on your comment to add another functional version of C to the
discussion, Single Assignment C.

[http://www.sac-home.org](http://www.sac-home.org)

[http://en.wikipedia.org/wiki/Single_Assignment_C](http://en.wikipedia.org/wiki/Single_Assignment_C)

------
pmarreck
Related: Here is a really good blog post by John Carmack after forcing himself
to code functionally in C++ for a month:

[http://www.gamasutra.com/view/news/169296/Indepth_Functional...](http://www.gamasutra.com/view/news/169296/Indepth_Functional_programming_in_C.php)

Some really choice quotes in there.

~~~
kartD
And here's one by Jonathan Blow as well (references Carmack's post as well)
[http://number-
none.com/blow/blog/programming/2014/09/26/carm...](http://number-
none.com/blow/blog/programming/2014/09/26/carmack-on-inlined-code.html)

------
naasking
I don't like the approach here, and the fact that it's gcc-specific. Many
years ago I wrote a small macro library for discriminated unions/sums which I
think is much cleaner and looks like actual C [1].

With some fancier variable macros + C11, you can even make closures in C
viable [2].

[1] [https://github.com/naasking/libsum](https://github.com/naasking/libsum)

[2] Incomplete, but both curried and uncurried closures are possible:
[https://github.com/naasking/cclosures](https://github.com/naasking/cclosures)

------
jonatack
I was initially hoping this would be about the book "Functional C" (Hartl &
Muller, 1997).
[https://www.amazon.com/dp/0201419505/](https://www.amazon.com/dp/0201419505/)

------
kbart
Nice experiment, but anyone writing such code in production should get his
fingers chopped.

~~~
Xophmeister

        $ chgrp sane_c_devs /usr/bin/gcc
        $ chmod 0770 /usr/bin/gcc
    

Of course, the following scenario is not impossible ;)

    
    
        $ getent group sane_c_devs | cut -d: -f4 | tr , "\n" | wc -l
        0

------
TickleSteve
This is as bad an idea as 'OO' C.

having had to deal with the grief that was a late-90s attempt at OO C.... stay
away from this.

Tooling is designed for idioms of the language. Attempting to be 'clever'
means you have to throw away all the benefits of that tooling as well as
reducing your maintainability and confusing the optimiser.

~~~
jjnoakes
Plenty of successful projects use some form of 'OO' C. The Linux kernel is
probably one of the most successful.

I'm not saying all C programmers should use OO techniques, and I'm not saying
all OO programmers should reach for C, but I don't think blanket statements
like "OO C is a bad idea" and "stay away from [OO C]" are useful without
context (and with context they are almost meaningless).

~~~
TickleSteve
The linux kernel is an extremely mild form of OO. Mild to the extent that is
effectively idiomatic C code.

What I'm referring to is the manual creation of virtual pointer tables and
dereferencing _everything_ through it. This completely defeats the optimiser
as it can't see thru the pointers. The case I'm referring to was a complete
WTF in hindsight.

~~~
jjnoakes
What is the difference between what you are calling virtual pointer tables and
what the kernel does in a lot of places?

[http://lxr.free-electrons.com/source/fs/ext4/symlink.c#L93](http://lxr.free-
electrons.com/source/fs/ext4/symlink.c#L93)

~~~
TickleSteve
Its the extent to which it is done.

virtual pointer tables are an age-old 'C' mechanism of providing runtime
configuration for drivers and such like.

the WTF project, took that idea and had virtual pointers for _everything_
methods, data, superclasses, etc.

Every line consisted of something like the following:

obj->vptr->methodA(obj, objB->vptr->methodB());

it very rapidly becomes a readability and maintainability nightmare.

~~~
jjnoakes
Sorry, I'm still not following your argument.

~~~
TickleSteve
dereferencing the vptr in the Linux device tables tends to happen once per
(say) function.

the WTF project would have '->vptr' and worse '->super->vptr->method(), maybe
20 or 30 times per function.

 _every_ call in the codebase or data reference would be via a '->vptr'.

That really makes a difference.... its unreadable. literally.

~~~
jjnoakes
So based on one project's misuse of a technique (OO in C) you dismissed it as
"bad" idea, "stay away from this", it's "attempting to be clever", and the
rest?

I think that's why I'm not following you. You've dismissed OO in C in its
entirety, and advised others to do the same, based on your experience in one
project which misused/overused the feature.

~~~
TickleSteve
no.

The level to which it is used in Linux is effectively idiomatic.

The project I'm referring to used the technique properly. Its just a bad
technique.

~~~
jjnoakes
You've said it is a bad technique no matter how it is used, and you have said
it is an ok technique if used at some levels (like the Kernel) but not others
(like the WTF project).

So you can see how I'm confused about what your opinion really is, I hope.

------
machuidel
I did the following experiment a while ago (map and flatmap in C):

[https://gist.github.com/machuidel/d7cc099ddc4970c6ddf4](https://gist.github.com/machuidel/d7cc099ddc4970c6ddf4)

By adding more abstractions it should even be possible to support semigroups,
monoids etc.

Of course this was just for fun.

------
djcb
In the same spirit, I wrote some functionally-flavored extensions for GLib a
few months back.
[https://github.com/djcb/gxlib](https://github.com/djcb/gxlib), taking a bit
of inspiration from Rust and SRFI-1.

It's fun to get some of the functional flavor, while still being very near the
bare pointers.

------
norswap
I wonder how the "discriminated union" thing is better than an enum + a
structure for each type + a structure with a field for the discriminator (the
enum) and an union of all structures. It certainly is much more readable, and
idiomatic. And this doesn't add any benefits that I can see.

~~~
dllthomas
That's exactly what it's doing. There's no benefit to using these macros
except that it's shorter and maybe (?) therefore clearer.

~~~
jonathankoren
It's certainly not clearer if we have to guess.

~~~
dllthomas
Guess what?

------
ricksplat
Sorry, I hope nobody minds a humorous posting (a quote from meet the parents):

 _Dina Byrnes: I had no idea you could milk a cat!_

 _Greg Focker: Oh, you can milk just about anything with nipples._

 _Jack Byrnes: [He reacts] I have nipples, Greg, could you milk me?_

~~~
Bromskloss
> I hope nobody minds a humorous posting

Not at all. Please go ahead and post it.

~~~
ricksplat
Okay here goes:

 _> your mom_

------
kensai
I so much adore the fact that you can do all paradigms with C relatively
easily.

~~~
Retra
I'm confused... what do you mean 'easily'?

------
nathell
(2013)

------
toomanythings4
I know things can go downhill quickly when I read things like this:

>There is one irritating thing about C as a viable programming language.

Given its history, I don't see where this guy comes from that he can declare
whether C is a viable programming language or not. Especially when his real
complaint is not about C at all.

>Microsoft’s compiler support is not good.

While some may complain that I'm making a minor quibble, my point is, when you
make statements like this, your credibility falls a notch or twelve and I have
to look at the rest of the article with suspicion.

~~~
MichaelGG
Viable in this case means for FP, I'd guess. MS's C compiler support has been
crap. In 2013 they didn't have C99 support, right? Just like he says. Even now
it seems their C support is driven by the C++ requirement.

------
PieterH
On Windows one compiles C99 code as C++, that's all.

~~~
xvilka
Since VS2015 MSVC should have most of C99 support already. So there are no
reasons to avoid C99 usage nowadays at all.

~~~
pjmlp
VS2015 MSVC supports C++14, which requires C99 library support, hence why
Microsoft updated the support.

Likewise MSVC will support C11 libraries when they get around to be fully
C++17 compliant.

For any newer C standard the official Microsoft position is to use clang with
the Visual C++ backend, called C2 which Microsoft is turning into a backend to
be shared between clang, Visual C++ and .NET Native.

