Live data from Hacker News

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

sevangelatos.com

81–90 of 179 posts

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

#81

As a 25+ years coder who's very accustomed to thinking in my own ways, I have a problem with functional-purism similar to the problem I had with globals and factory functions. The fact is that most programs have a lot of states and these states have to be represented somehow. Allowing the objects to contain functions that act on those states is a perfectly good analogy for most business logic or game logic you're mod…

> these states have to be represented somehow In data types! > But so many functions are more comprehensible when they take place bound to the scope of what it is they're acting upon To me, the most understandable function (lowest context needed, lowest cognitive overhead) is a black box that takes type x as input and produces type y as output, without side-effects > I don't think that thinking in terms of objects or…

> In data types!

Yes. With guardrails. I mean, I do a lot of writing in JS/TS but I'd never write something that tacked a dynamic property onto some class and picked it up later. That's just gauche.

> a black box that takes type x as input and produces type y as output

I agree, but I'd rather keep that function in Class X. If it's specific and X is a final/protected class, I'd say `protected doSomething()=>Y` where the argument is the implicit `this`.

I'd never allow some other thing to run this function, definitely not some other object floating out there; if it were going to be run that way it would have to be a `static DoSomething(x:X)=>Y` ...but I would still keep it in the file for X.

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

#82

In terms of applying this to languages other than C++, as someone who’s relatively new (4 years) to Java I expected to see a lot more side effects in the Java I’ve worked with in that time. In fact, the opposite has been true, and much of the points discussed in this article seems to have been adhered to; even going back to some of the older codebases. That said, we mainly have stateless microservices running on k8s,…

Now that Java has algebraic data types and pattern matching, it explicitly espouses a programming style long associated with FP, especially when programming in the small. See Brian Goetz's (Java's chief language architect) article: https://www.infoq.com/articles/data-oriented-programming-jav...

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

#83

Earlier quoted context omitted.

It took me awhile to realize but this is one of the big things that algebraic data types (ADTs) help you to do...design your data types so they have exactly the number of valid states that your domain has. To use another common FP way of saying it...make invalid states unrepresentable.

A classic way to do this, is enumerations.

The nice thing about ADTs is that you can couple the state variables with the enum, so that the representable state is better tied to the enum value. (Yes, you can kind of do this in C with a discriminated union of a struct holding an enum and a union, but the type system doesn't stop you form grabbing a union member that doesn't match up with the current enum value.)

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

#84

> 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…

The problem is even bigger when you are reviewing PRs, the lack of context is the big problem,imo.

I agree.

Good code design, regardless of paradigm used, encapsulates complexity in order to minimize the amount of context needed to understand any one bit of code. The better the encapsulation, the better we can scale our limited mental capacity to larger and more complex systems.

If the complexity is better encapsulated, it's also faster and easier for the reviewer to acquire the necessary context.

When I was first learning OOP, I wish someone had mentioned why classes are useful: they leverage our natural mental abstraction machinery for understanding the world. Walking down the road, we generally think of each car as a whole and ignore thinking about the thousands of parts that make it up. It would be overwhelming to simply cross a busy street if we were considering all of the millions of car components flowing down the streets or considering the various differences in cars all the time, rather than abstractly thinking of various differing collections of parts as just "car". All of this happens at a subconscious level, and we can consciously tap into some of this mental machinery to help us reason about code. Of course, we don't need OOP to leverage this mental machinery, but it's one technique for doing so.

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

#85

> 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…

> 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 thinking through the side effects of the change That's a great take, but I'd turn it upside down. Problems come from developers adding new features while not being mindful of the importance of helping out whoever is reading that code in the future to understand what are all the possib…

One thing I've seen that helps out immensely with this in a large complex code base is extremely liberal use of ASSERTs.

Every time you have a function or clause that takes an object or data with many possible versions or states, but only will work correctly with particular assumptions, ASSERT that your assumptions are satisfied. Every time.

This helps in several ways. At the time of writing the code, it makes you think about possible states that could be represented and the generality of what your new code actually handles, and so encourages you to write general solutions and avoid unhandled or mishandled cases.

At the time of reading the code, it helps the reader see what cases the writer considered. It helps document the intended scope and use cases the code is designed for.

At the time of extending surrounding code, it helps to catch cases where the extension introduced state combinations that had not been considered before. Its nice to make an extension, run a test case, and get an assert that tells you exactly what other function needs an extension. Without the assert you would make the extension and run your test case and get wrong behavior or a crash and have a big debugging mystery or even worse: apparently working but subtly wrong code.

Others in the thread are talking about making invalid states unrepresentable, which is nice, but not always doable. In a big code base you don't have the luxury of redesigning things; usually you have to stick to more local changes. But you can always add ASSERTs -- that is a completely local way to mark invalid states as invalid.

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

#86

Earlier quoted context omitted.

It took me awhile to realize but this is one of the big things that algebraic data types (ADTs) help you to do...design your data types so they have exactly the number of valid states that your domain has. To use another common FP way of saying it...make invalid states unrepresentable.

A classic way to do this, is enumerations.

Hence why they are called enums in Rust.

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

#87
post #83

Earlier quoted context omitted.

A classic way to do this, is enumerations.

The nice thing about ADTs is that you can couple the state variables with the enum, so that the representable state is better tied to the enum value. (Yes, you can kind of do this in C with a discriminated union of a struct holding an enum and a union, but the type system doesn't stop you form grabbing a union member that doesn't match up with the current enum value.)

I use Swift.

Swift enums are really nice[0].

[0] https://littlegreenviper.com/miscellany/swiftwater/enums-wit...

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

#88

Earlier quoted context omitted.

In the same interview he also mentions using python a lot lately and making the point that for the vast majority of code, performance really doesn't matter and productivity is more important. Reading this article, he's making a great argument for Rust. I assume the article predates rust by quite a bit. But basically Rust is not a pure functional language but still has a lot of elements in the language that add purity…

>In the same interview he also mentions using python a lot lately and making the point that for the vast majority of code, performance really doesn't matter and productivity is more important. Until it does matter. Then it *really* matters at which point you're normally looking at a bottom up rewrite and likely retooling and rehiring for new skills. Ive been bitten by "oh it's just a prototype" a few too many times n…

I guess it might be my lack of experience, but why are rewrites bad ?

It's a prototype because when you start a project :

- you don't understand what the client wants

- the client themselves don't really understand what they need (and sometimes, not even what they want, which is magnified as soon as you have to deal with multiple people)

- you might not know how to achieve that

- you might have an idea how to achieve that, but not have practical experience in it

- reality (including funding limitations) restricts what solutions can be implemented

- the usual code entropy...

Given all of the above, why would you NOT do a bottom up rewrite at least once ?!

P.S.: And in the cases the performance critical bits can be isolated, Python can outsource those to performant languages like FORTRAN or C.

I am guessing the answer usually is : the funding doesn't come directly from the end user, and so the incentives are not aligned for a successful project - which also explains why software projects have such poor success rates (~10%) ?

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

#89
post #85

Earlier quoted context omitted.

> 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 thinking through the side effects of the change That's a great take, but I'd turn it upside down. Problems come from developers adding new features while not being mindful of the importance of helping out whoever is reading that code in the future to understand what are all the possib…

One thing I've seen that helps out immensely with this in a large complex code base is extremely liberal use of ASSERTs. Every time you have a function or clause that takes an object or data with many possible versions or states, but only will work correctly with particular assumptions, ASSERT that your assumptions are satisfied. Every time. This helps in several ways. At the time of writing the code, it makes you th…

Haskell case expressions are nice. It seems like you can define which input values a function can process - and you can get a log of if/else logic removed.

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

#90

Earlier quoted context omitted.

>In the same interview he also mentions using python a lot lately and making the point that for the vast majority of code, performance really doesn't matter and productivity is more important. Until it does matter. Then it *really* matters at which point you're normally looking at a bottom up rewrite and likely retooling and rehiring for new skills. Ive been bitten by "oh it's just a prototype" a few too many times n…

I guess it might be my lack of experience, but why are rewrites bad ? It's a prototype because when you start a project : - you don't understand what the client wants - the client themselves don't really understand what they need (and sometimes, not even what they want, which is magnified as soon as you have to deal with multiple people) - you might not know how to achieve that - you might have an idea how to achieve…

“It’s just a prototype,” can be bad if you are doing half baked things to get them done quickly, telling yourself you will rewrite for production.

A pattern I’ve frequently seen is schedule pressure mounts and when you need to scale back on scope to meet a deadline, rewrites are the first to go.

I’d guess a good portion of the garbage people inherit from others fits into this category.

Post reply on HN