Live data from Hacker News

John Carmack on Functional Programming in C++ (2018)

sevangelatos.com

61–70 of 179 posts

Re: John Carmack on Functional Programming in C++ (2018)

#61

> A large fraction of the flaws in software development are due to programmers not fully understanding all the possible states their code may execute in. I think this probably the most important concept for programmers to keep at the front of their minds when working day to day. So many of the problems I see come from developers (myself included) adding new business logic to a process, or fixing a bug and not thinkin…

> adding new business logic to a process,

Adding logic is the smallest part of the problem: adding types, that aren't constrained to represent the smallest set of values needed for the problem at hand, that's the biggest problem. This 'looseness' doesn't show up as a compilation error and looks on the surface to be just fine. Then the real-world comes along and creates a value that should never exist and the code falls over.

Constraining the data by properly defining types is the (primary) way to reduce the possible states an application can get in. This can be via sum-types (discriminated unions), or even making sure you don't use `int`, `string`, etc. and other types that are just one step away from being dynamic.

When doing pure functional programming, along with properly constrained types, you tend to find the code writes itself. What I mean by this is that it becomes very much about composing functions via 'type lego'. Once the types fit, your code works [1]. This is a very profound feeling once you grok it, because it gives oneself a feeling of confidence in ones own code that doesn't happen in imperative land. I will sometimes go days or weeks without even running the code I'm writing because I just have confidence in it.

[1] It's still possible to write logic errors, but they're the only major class of bugs you get really.

Re: John Carmack on Functional Programming in C++ (2018)

#62
post #39

Earlier quoted context omitted.

The argument isn’t that state is bad or doesn’t need to be managed. It’s more that spreading that state over a large number of areas leads to a lot of complexity and cognitive overhead in terms of expected behaviour at any given point of execution. Functional programming gives you ways to be much more explicit about the transformation being performed and the before/after states (except arguably when you start getting…

Isn't encapsulating state - and functions that act on states - within the smallest-possible class ancestor a pretty reasonable way of handling that? I'm not suggesting spaghetti or`GOTO 10400` or `GlobalCatBehaviorFactory.StartMeowing(cat)` or something. I mean we have a paradigm, and it's basically OOP with a dash of functional programming at the functional level. Maybe what I'm trying to say is that a lot of functi…

I would say it's often the opposite, unless I'm misunderstanding you. Instead of being encapsulated "deeply" in a class ancestor, state in FP is in the "top" of the program, in the "shallow" part, as explicit as possible and as close to the entry point as possible, while the functional part is the thing that is in the middle. The term for this is "Functional core, imperative shell". In something like Haskell, side-effects are dealt with even "before" the entry point: they're handed by the runtime, which is the part that calls the entry point.

This also translates well to OOP in the form of things like hexagonal architecture (eg: business logic is pure, and state is modified by a database adapter), or to modern frontend programming (eg: the render tree is pure, visual representation state is stored/modified with by React), including flux architecture (eg: reducers are pure, state is stored/modified by Redux).

I really like this presentation about it: https://www.destroyallsoftware.com/screencasts/catalog/funct...

Re: John Carmack on Functional Programming in C++ (2018)

#63
post #10
post #5

Earlier quoted context omitted.

The language does not offer the features that you were referring to, and that the author of the article mentions. You can write pure functions, but the purity is not checked by the compiler, that is what has been meant here.

yes, i know. but there is nothing that stops you from writing pure functions, and quite a lot of features that encourage it, even if they are not checked by the compiler. i'd probably like a c++ compiler that warned me about lack of purity, but as the article says, sometimes you have to bite the bullet.

> there is nothing that stops you from writing pure functions

If they are not checked, they will be forgotten. When reading a function, if I do not have the guarantee that it is pure, it means the same as an old comment (not much).

If the only way to enforce it is convention and force of will it will not be enforced.

As an exaggerated illustration: we might add a "without-bugs" annotation to a function, to indicate it has no bugs. After all, nothing stops you from writing bug-free functions :)

Re: John Carmack on Functional Programming in C++ (2018)

#64
post #4

best article i've seen on pragmatic purity - i would only dissent on c++ not providing facilities for pure functions (i don't here mean pure in the virtual=0 sense) - you can, and should, do quite a lot.

GCC has a pure attribute for functions which is documented as being prohibitive of impure behavior. Though, I honestly cannot say how thoroughly it is enforced or if there are significant benefits outside of the obvious and some minor optimizations which the compiler might be able to perform.

It's not enforced. It's a promise. If you mark a non-pure function pure, then you are going to have a bad time.

I would also be reluctant to mark any function pure that takes pointers as arguments (directly or indirectly).

Re: John Carmack on Functional Programming in C++ (2018)

#65
post #38
post #37

Earlier quoted context omitted.

why do you think the length member function is not pure?

If you read the article and see what pure functions are, you will understand. The length function has no parameters, by definition it must access data outside of the function if it’s going to do anything. This means it is not pure. Can you take that length function outside of the class and run it and have it do something? No, it is tightly coupled to the class that it is attached to.

Member functions effectively have the object as the first parameter.

Re: John Carmack on Functional Programming in C++ (2018)

#66

> A large fraction of the flaws in software development are due to programmers not fully understanding all the possible states their code may execute in. I think this probably the most important concept for programmers to keep at the front of their minds when working day to day. So many of the problems I see come from developers (myself included) adding new business logic to a process, or fixing a bug and not thinkin…

this is why I think tests are so vital.

good luck trying to fix a bug in a complex project without doing some sort of test to make sure the change does not break one of the thousands of requirements implements so far.

from my experience fixing a bug without validating the system has a very high chance to spawn multiple bugs found later on...

Re: John Carmack on Functional Programming in C++ (2018)

#67
post #65
post #38

Earlier quoted context omitted.

If you read the article and see what pure functions are, you will understand. The length function has no parameters, by definition it must access data outside of the function if it’s going to do anything. This means it is not pure. Can you take that length function outside of the class and run it and have it do something? No, it is tightly coupled to the class that it is attached to.

Member functions effectively have the object as the first parameter.

Another commenter pointed that out, and you can see my other reply regarding that.

Re: John Carmack on Functional Programming in C++ (2018)

#68
post #55
post #51

Earlier quoted context omitted.

i find this slightly patronising - i have studied languages such as scheme and haskell. and i don't see how writing a function such as length(string) could be pure if the member function length() was not, as you would pass a copy of the string (ok, pure) and then call a member function (impure, according to you) on it to actually get the length.

No, you would not typically call a member function on anything in a pure language. Instead, you would pass a sequence of characters and the function would return the length, by counting them. I didn’t mean to sound patronizing, I apologize for that. Your question suggested that you aren’t that familiar with functional programming, so I was just trying to steer you in the right direction. A function that takes no para…

well, the length function for c++ strings does return a constant object - an integer value.

and passing a sequence of characters? hello, C strlen( const char * s); ! with all the problems that has. and if you don't want to pass some horrible pointer, you have to pass some object containing the characters which must have a length function itself. hello, std::string!

what you are ignoring here is how functional types either ignore functionality under the hood, or sacrifice it for performance.

which is why functional programming will never catch on (look at lisps), imho. programmers really like to know wtf their code is actually doing on a mutable machine.

Re: John Carmack on Functional Programming in C++ (2018)

#69
post #62

Earlier quoted context omitted.

Isn't encapsulating state - and functions that act on states - within the smallest-possible class ancestor a pretty reasonable way of handling that? I'm not suggesting spaghetti or`GOTO 10400` or `GlobalCatBehaviorFactory.StartMeowing(cat)` or something. I mean we have a paradigm, and it's basically OOP with a dash of functional programming at the functional level. Maybe what I'm trying to say is that a lot of functi…

I would say it's often the opposite, unless I'm misunderstanding you. Instead of being encapsulated "deeply" in a class ancestor, state in FP is in the "top" of the program, in the "shallow" part, as explicit as possible and as close to the entry point as possible, while the functional part is the thing that is in the middle. The term for this is "Functional core, imperative shell". In something like Haskell, side-ef…

Um... ok. I think we're sort of both talking about keeping logic unbound from artifacts like display and from objects that contain state. Where I think OO makes sense is that once you admit that objects have properties it makes sense to take the top form of that object and put the functionality there - rather than abstracting it away into some completely other logical place.

Maybe this all comes down to what I find easier to follow in 200 code files and we're talking about roughly the same thing. If the general rule is to make logic as independent and no-need-to-retype-it as possible in the hierarchy, I'm onboard. And letting functions manage state themselves makes no sense; even in OO paradigms, functions should be pure and not rely on the state per se. (Put in plain terms, I make heavy use of getters and setters on instances; but complex functions are almost always static, and take instances of the class they're in to modify, rather than reproducing those functions and having them work on their own instance).

Re: John Carmack on Functional Programming in C++ (2018)

#70
post #68
post #55

Earlier quoted context omitted.

No, you would not typically call a member function on anything in a pure language. Instead, you would pass a sequence of characters and the function would return the length, by counting them. I didn’t mean to sound patronizing, I apologize for that. Your question suggested that you aren’t that familiar with functional programming, so I was just trying to steer you in the right direction. A function that takes no para…

well, the length function for c++ strings does return a constant object - an integer value. and passing a sequence of characters? hello, C strlen( const char * s); ! with all the problems that has. and if you don't want to pass some horrible pointer, you have to pass some object containing the characters which must have a length function itself. hello, std::string! what you are ignoring here is how functional types e…

> the length function for c++ strings does return a constant object -

No, I mean, a standalone function with no parameters can only be functional if it always returns the same value, which is effectively no different than declaring a constant. The function always returns the same value all time (which wouldn't be so useful).

I think you are trying to find C++ examples to match the broader implications of functional programming (which is what I'm talking about), which will be difficult.

As far as whether "functional programming will catch on", I don't think it will for C++ so much, but it certainly has been very successful for many other languages. I earn my living writing only functional code, but most of the time it is not in C++.

Post reply on HN