All programming languages and all programming styles can be similarly dismissed as encoding one Turing-complete form into another. Why pay attention to any of them?
This article is clearly someone having fun with their language. It isn't a serious claim about C being functional, something which the author states explicitly if you read to the end.
I don't think that solves the problem. Whatever fixed stack size you choose, you still cannot write a program that determines the size of the lists at runtime. A Turing complete system would have to be able to do that.
The only way I see is to allocate (almost) the entire machine memory to the stack, create one giant array in that chunk of memory and then put all lists in that single array. That's tantamount to reimplementing malloc on the stack.
Sure, but we're talking about whether or not this particular style of C can be Turing complete and I'm not sure it is, even on a machine with an infinite amout of RAM.
C arrays that live on the stack have to be created with a constant size (at least before C99). It's a limitation of the language regardless of any machine or stack size.
I would say the aspect that defines a functional programming language is the support of higher order functions. I.e. functions that can take functions as arguments and more importantly can return functions as return value.
As jeremyjh already pointed out, this results in a pretty weak definition. You end up with JavaScript, Ruby, Python, and Objective-C all being lumped as "functional languages", and if you squint a little bit, you can put Java in there too (anonymous inner classes). I think C, C++ and C# could be as well, but I don't know them well enough.
So I don't think it's very productive to use a definition like this, but not because I want to be divisive, or "move the goal posts" or because I'm trying to be elitist here. It's because when a colleague asks you "what's functional programming?" I think it's much more helpful to describe the kind of programming that is encouraged in Haskell and Clojure than to just say, "well it's just map, filter, and fold in JavaScript".
Or we could be more precise and acknowledge there are several related features in the realm of "functional" and a language may have some but not all of them, and not make it into a fan club battle.
Same goes for OO, which has something like 9 well-known defining features.
I don't think my comment was making anything into a "fan club battle". I was specifically advocating the kind of precision you describe, because I think calling any language with higher-order functions a "functional programming language" results in very muddy waters.
The scenario I described wasn't hypothetical. When a colleague asked our whole office "what's functional programming?" I listened while someone told him that it was just map, filter, and fold. He'd used these in JavaScript already, and was prepared to accept that as an answer. I didn't think that was sufficient, so chimed in and offered some alternative features from other languages that I think are as important as higher order functions, mostly related to minimizing mutable state with managed references and persistent data structures. I think he appreciated my contribution, but I honestly don't know for sure. Maybe it just created more questions.
Rather than saying that higher-order functions is the defining feature of functional programming, I would say that it's an essential feature to support its object of defining programs in terms of (mathematical) function application rather than procedural state changes.
Does any higher order function application require state changes to implement as a specific lower order instance.
For example, an instance map is just a cons and a recursive call. No state change needed. You can say something about exposing unwanted details in the cons, but that is a hairier point of contention.
I'm not that old but I remember that, back in the days, functional meant just that you are able to use plain functions (without objects) and pass them around. By that definition, C is functional language, as scheme or lisp or javascript and many others.
I don't know why, but some years ago, "functional programmers" started to change and twist the definition of functional language step by step, until all those languages fell off and only Haskel remained as "functional enough".
I always thought the fundamental difference between a functional and imperative language is that a functional language is built up from expressions, whereas an imperative one is built up from statements.
I think you at mixing words that look similar. It is a large problem in this sort of discussion:
Pure functions. (Only Haskell and friends are strictly pure, as is SQL if you squint.)
Higher order functions. (Java 7 doesn't allow you to pass around a callable function without embedding a link language with reflection or using one-method Runnable.)
First class functions. (Basic, SQL do not have this.)
Generic functions. (C does not have this. C++ does. Java does.)
These are three orthogonal properties a Lang age may have some or all of.
Sure you can - just not anonymously. Kind of like how you can have "higher order functions" and "closures" in Java with anonymous classes - roughly equivalent, but awkward and not quite what the language was designed for. C is probably better in this regard than Java, but the lack of managed memory is a major drawback.
Right. SO you can program 'functionally' in those languages, but they are not 'functional programming languages' because they don't support it natively.
Just not depending on anything in the environment either except globals, which is a huge limitation. So writing a function
inc by = \x -> x + by
becomes unnecessarily difficult for example.
I meant to refer to extensions implemented independently by one compiler or another. Clang and gcc both provide lambdas, but not in the same way, and there's no standard for it. (I could be wrong on both points; this is purely from memory, and could have changed.)
Actually, if you look closely you'll notice that not other language lets you do this either (except through an 'eval' or similar). What I think you actually mean is that this still doesn't let you use closures. But in fact it does! :D You'll have to do a lot of void * casting to make it work though.
C does not provide closures. You can pass around a struct with a function pointer and a data or state pointer though. That will let you achieve the same thing, but it won't be as pretty.
I think that this is an interesting area. We can support just about any style in an existing programming language, but there are often rough edges.
This reminds me of how I used to mimic OO in C years ago. At a certain point you realize that you have to forgo certain things to help the person coming after you avoid making mistake -- often they do not see the ramifications of your conventions.
I see this in Ruby right now when I write in a functional style. The code is often terse yet easy to read, but the lack of lazy evaluation makes it far more memory consumptive than code written in a straightforward procedural style. If you know you are making the tradeoff, it is fine. For many people, it is too subtle.
From that it looks like the caller always has to allocate the right amount of space on the stack to hold the result. What if the result size is known only by the callee?
Denying yourself the use of the heap is also going to make closures rather difficult. You can use this style to pass back a function pointer, but the caller would also need to allocate space for the callee's captured variables. Ick.
I disagree completely that this is a 'functional subset' of C. I also don't know a lot about this stuff, so tear me up.
A functional language isn't just one that doesn't use malloc/only uses the stack. It must actually not have state, and instead evaluate a single function down to the result. Here, after calling make_lists, the input and the result still both exist as state variables (immutable, yes, but that's not enough)! At the same time, I haven't looked at code for a lisp interpreter, but I feel safe assuming that it allocates some memory somewhere. Functional programming doesn't have anything to do with the underlying implementation. The trick here, if any, is that the "underlying implementation" and the code are in the same language/program.
I use clang with a -Wall and -Wextra, which will complain about all sorts of stuff I generally want to know about, including unused variables (I think gcc does the same?). For the rare cases where I actually want to leave a variable unused I do the above to make the warning go away.
At some point the stack is going to grow which will cause allocation at the OS level. And if we're discussing small embedded systems with fixed stacks, this code is entirely unsafe (non deterministic stack usage may cause stack overruns).
In embedded systems (non-MMU ones) you generally want to avoid repeated dynamic runtime allocation to prevent memory fragmentation.
It's a cute example, but I can't see any scenario where its better or safer than heap use.
Ditto on the addictiveness of continuation-passing style.
However, don't try it when coding with peers who are not used to it; you can be burnt at the stake. Because, even though it makes the code easier to read, to the untrained eye it is just cryptic.
"So my title is misleading. I don’t think C is a functional language. But it’s an awful lot of fun (and sometimes very useful) to use C’s functional subset."
it might have also been worthwhile to mention that the stack array declaration:
int32_t result[my_array_size];
relies on C99 support, which is not necessarily ubiquitous (especially in embedded device work). At the very least, many compilers make you explicitly enable C99 support.
GCC can eliminate tail recursion, so does that make C functional?
I don't think the author is seriously of the belief that C is a functional language. This is just a fun little example of writing C in a functional style.
With foldl', of course. If you don't have tail recursion elimination, foldl' would have to be provided as a built-in.
Summing up numbers is inherently strict, so you'd want tail call elimination for that. But functional mainstains, like say, map or filter are usually not implemented with tail recursion in Haskell, because that would be too strict and would break on infinite lists.
I think you might have misinterpreted the contents of that page. As someone who has worked on GHC, I can assure you that it does perform tail call optimisation.
1: http://en.wikipedia.org/wiki/Chicken_Scheme
2: http://wiki.call-cc.org/chicken-compilation-process
3: The homepage is called www.call-cc.org :P