7 ms·
Functional Programming in C++ (2013)
- astazangasta 11y agoI just can't get behind all of this. Sure, its great to be able to reason clearly because you can see your whole state, but its exhausting always carrying your context around like a hobo with a huge backpack full of junk. Am I mistaken?
- saryant 11y agoExhausting is being in this situation: object Foo { var x: Int = 0 def bar(y: Int) = x + y } And trying to figure out what bar will return at any given point.
- pekk 11y agoAre you somehow not in control of how x is being changed? You seem to have said that literally any statefulness is "exhausting".
- gnuvince 11y agoAnyone, anywhere could modify x, which is what makes reasoning about this code difficult. When your entire program is a collection of mutable state that can be mutated by any piece of code, understanding what's going on becomes really difficult if not impossible. One thing that people are adopting from FP is to limit what is mutable, and when possible make state explicit.
- pekk 11y agoThat isn't true. I don't understand why you can't use a language that offers encapsulation.
- S4M 11y agoWhat convinced me that avoiding mutations as much as possible was having to deal with code like that (in java): public class MyBigClass() { private int a; private int b; //hundreds of lines of code private int calculateSomething() { //do something a = a+1; return b*b; } //hundreds lines of code private int calculateSomethingElse() { int c = calculateSomething(); return a+c; } //hundreds of lines of code public returnSomething() { return calculateSomethingElse(); } } In this example. It's hard because you have to remember constantly which functions are changing your classes before you use them, and if you haven't written the functions, it's very easy to introduce a bug by involuntarily modify the states using the function calculateSomething, just wanting its output and not the modification of a.
- pekk 11y agoThat demonstrates some design flaws of Java, yeah, because your "private" variables are effectively just globals anyway. The guy who is not using encapsulation at all this year will be making similar mistakes (massively long bodies, proliferation of unnecessary entities, etc.) if he switches to functional programming next year. It's not a panacea. I'm afraid this doesn't demonstrate that any tiny bit of statefulness is "exhausting."
- eli_gottlieb 11y agoThe monad type-class operations carry the context around for you. You can just write in imperative-seeming do-notation. The difficult part is trying to combine monads.
- sago 11y agoHere's what I don't get with Monads, why I've not felt the need to create a Monad-like system in my (non Haskell) code. The 'world' is carried on the back of one parameter, right? So if I want to run a function taking two parameters in a particular 'world', which parameter is the Monad? If both of them are, what if they disagree which world is used? Perhaps I haven't reached the fabled epiphany of monads, but they seem to be just implicit state attached to a single value, and only a single value. I'm not sure what this implicit stuff is better than a system where you are explicitly passing the 'world' that each function needs. Why is roll(dieSides: Random<int>) -> int Clearer than roll(random: Random, int dieSides) -> int I get the passing of a world out of a function with a single value getNumber() -> IO<int> or using the extra implicit state to represent other possible values, like Maybe<int> but, using it to carry around state? That seems to me to be unnecessarily implicit. What am I missing?
- prodigal_erik 11y agoIn a language with immutable values and pure functions, roll always returns the same number from the same generator. If you want to call roll twice and get different numbers, the first call needs to return a new generator (with an updated seed) to pass to the second call. Rather than having to write something like do (x, g2) <- roll g1 (y, g3) <- roll g2 (z, g4) <- roll g3 it's convenient to have plumbing that hides all those single-use g values.
- sago 11y agoThanks. So the issue is that Monads save us having multiple returns? That makes sense.
- 11y ago
- 5outh 11y agoWell, sort of. One of the big focuses of functional programming is _minimizing_ state; in other words, only what must be passed around should be. Sure, you'll end up having a large context that the entire program operates in, but having to pass around a context incentivizes passing around the minimal amount of context to sub-parts (functions) in the program. You don't need the whole global state? Great; only pass around a small part of it to that part of the program. You don't need the whole context all the time so you don't pass the whole thing around all the time.
- peterfirefly 11y agoKinda. There are tricks to make it less burdensome. One of the tricks is the use of monads in Haskell. Monads are really just a small set of functions that operate in a certain way (that "satisfies the monadic laws") on a type. What that gives you is (among many other things) a way to carry an implicit "world" around with you without needing to refer to it all the time. One use for such a "world" would be a seed for a random number generator so different parts of your program can use their own, isolated streams of random numbers (nice for determinism, testing, debugging, etc), even if they execute asynchronously because Haskell is a "lazy" language where code doesn't necessarily get evaluated unless it really needs to. Another use is for the "world" to actually represent the real world -- all of it -- so the I/O interactions happen in the right order. That's very important in a language where the evaluation order is so "loose" as it is in Haskell. There are other tricks as well. One of them is to have lots of small transformation functions that don't need to understand all of the world, just their little bit of it. You create the big world transformations by composing the smaller transformations. The data structures you use are slightly different: you don't want to modify your data structures, you want to create new ones all the time, i.e. inserting an object into a tree shouldn't modify the tree but return a completely new tree with the new object in it WHILE KEEPING THE OLD TREE AROUND UNCHANGED. This doesn't have to be as expensive as it sounds, in fact, it can be quite cheap. Data structures where you can do this cheaply are called persistent data structures and they work really well with a functional programming style. (They don't have to implemented purely functionally internally as long as they present a pure interface.) https://en.wikipedia.org/wiki/Persistent_data_structure https://en.wikipedia.org/wiki/Persistent_data_structure Persistent data structures can also be confluent, which means that they use sharing internally if two versions of a data structure end up being logically the same. Tarjan and Sleator have some really nice papers on that -- well worth reading, even if you don't care one bit about functional programming.
- jcurbo 11y agoThis isn't a strict definition, but I just think of it as turning the usual model inside out. You always have a state that you're modifying, if you're doing functional or imperative programming (or a mix). Usually you're "inside" the state, changing it via pokes and prods from within. (Think global/shared state typical of C/C++ programs) In FP you pass around a big ball of state and no modification happens outside of it (there is no global or shared state, or at least access to it is tightly controlled if it's not a pure language). The functions live 'outside' the state, so to speak. Programming this way was, to me, one of the major revelations of learning the functional style.
- overpaidgoogler 11y agoOne difficulty with functional style c++ is just how verbose it is. Every time I tried to use std::transform (the equivalent of "map") I had to delete it and rewrite as a loop because it was just too hard to read. Functional style on a large scale is still possible.
- empiricus 11y agoThe article is not about the functional C++ you mention. It is about using pure functions (functions that do not modify global context).
- overpaidgoogler 11y agoI get that. But the two ideas are very closely related. See also my final sentence.
- phloxicon 11y agoI'm really hopeful that Concepts will dramatically reduce the amount of code needed for std::transform. Imagine no begin() or end(). It would suddenly become less verbose than the classic for loop.
- pcwalton 11y agoThat problem is due to the STL not being built around iterators/ranges. I don't see how concepts enter into it (though contra Andrei Alexandrescu, I'm still a big believer in concepts).
- femngi 11y agoIt really irks me that the std algorithms library seems to have been designed without any consideration for usability given how much I would love to rely heavily on the generic algorithms it contains. For example consider the simple task of testing to see if an element exists in a container: if (std::find(container.begin(), container.end(), element) != container.end()) My fingers are now cramping and my eyes are crossing trying to read that. It beggars belief that overloads don't exist which take an entire range which is all I want to do 99% of the time.
- brador 11y agoI've noticed hackers/self taught prefer functional programming styles and CS degree holders prefer the more common style.
- thesimpsons1022 11y agoi think people probably just prefer whatever paradigm they used first or currently are comfortable with. I have a cs degree and we were taught scala at my school and now I use it everyday.
- peterfirefly 11y agoI used BASIC first, then Z80 machine code, then Pascal, then 8086 machine code, then C. I really like functional programming, especially in Standard ML and Haskell. I didn't learn ML until I'd been programming for about 15 years. Edit: beadder grammer.
- agumonkey 11y agoI woudln't say 'prefer' but it's a heavy influence on your view of the world. I think imperative programming fit our biological needs and reflex. We want to see results and effects. Functional programmers are up high in the world of platonic ideals. They delay effects by composing abstract logics and then ask for a result. No newbie wanna suffer that, but no newb want to suffer mutable state bug hunts either.. the learning excitement just shadow this issue for a while, and I bet somewhere deep inside, we're all mathematician in the closet.
- varelse 11y agoI'm self-taught and while I can see the formal appeal of functional programming, I'm a low-level coder that implicitly reduces state to bare-metal registers and cache in GPUs and embedded systems. Since I'm already sweating bits over state down to the byte-level, I just don't see what FP would improve on this.
- peterfirefly 11y agoThe big improvements often come from better algorithms and data structures. Using a function style or a higher-level language sometimes makes it easier to modify the program (and hence use a better algorithm). I remember people swearing by assembly language back in the 80's, thinking C would make their programs slow and bloated. In reality, it was usually the other way around because C code is much more malleable than assembly. You might want to give FP a second look.
- vivekian2 11y agoOn an unrelated note, sad to see altdevblogaday has gone away :( Sure the pages are cached somewhere, but the posts always made for interesting reading.
- rootlocus 11y agoCouldn't agree more. It went away shortly after I discovered it, and although there's a lot of awesome information in the archives, it's a shame no new content is being added.
- vezzy-fnord 11y agoThis is from 2013, and the original title is "Functional Programming in C++". Moreover, Carmack never makes a statement that functional programming is the future anywhere in this article. The closest he says is "Formal systems and automated reasoning about software will be increasingly important in the future," which is a markedly different sentiment. That said, the problem of not understanding possible code states does not seem to be purely a result of the internal software structure, but how it interacts with outside components, including the OS (if any). There is a combinatorial explosion of the state space as a program's dependencies increase. As such, the most practical way to deal with this is crash-only software whereby components do not perform complicated error recovery, but are simply fenced and restarted to known good states with persistence used to ensure that the component can return to its prior point of operation quickly. FP is useful for writing software like this, but not necessarily pure FP or even a single paradigm.
- dang 11y agoThanks re the title. Submitters: the HN guidelines explicitly ask you not to rewrite titles like this, unless they are misleading or linkbait. https://news.ycombinator.com/newsguidelines.html https://news.ycombinator.com/newsguidelines.html (Submitted title was 'John Carmack: Why functional programming is the future'.)
- currysausage 11y agohttps://news.ycombinator.com/submit https://news.ycombinator.com/submit should have a link to https://news.ycombinator.com/newsguidelines.html https://news.ycombinator.com/newsguidelines.html. (Edit:) I know there is a link in the footer of every other page, but when you open the submit form in great excitement, it's easy to forget about that (as demonstrated by this submission).