My heart grew a little warm with the last paragraph of John Carmack's comment:
"The major evolution that is still going on for me is towards a more functional programming style, which involves unlearning a lot of old habits, and backing away from some OOP directions."
Here's a pretty trivial example in Haskell - computing the factorial function using a mutable variable.
import Control.Monad.ST
import Data.STRef
fact :: Int -> Int
fact n = runST (fact' n)
fact' :: Int -> ST s Int
fact' n = do a <- newSTRef 1
mapM_ (\x -> modifySTRef a (*x)) [1..n]
readSTRef a
Here the function `fact'` uses mutable variables (encoded in the use of `ST` in its type -- `ST` stands for State Thread) but the function `fact` is pure -- the call to `runST` ensures that none of the side effects leak out of `fact'`.
As with most Haskell code, the types are optional - I included them for clarity.
> As with most Haskell code, the types are optional - I included them for clarity.
I'd just like to make it clear to anyone else reading this. The types aren't optional, but because Haskell has type inference, specifying them is optional.
In Haskell there is the ST monad can be used to write stateful implementations for pure functions. The type system guarantees that side effects can't escape their scope.
Although this is great information I primarily had 'less pure' languages' in mind when I asked.
Come to think of it, restricting mutable access to only the relevant objects as per function call using const seems to allow just this!
A function having only const input and output "should be" free of side effects (if no global mutables); while still being allowed to run all manner of unpure processes local to its own calling context.
Clojure has "transient" (mutable) forms of its immutable datastructures, which can be bashed in place, but they're not allowed beyond function boundaries in either direction (I think). So you convert (copy?) to transient, mutate it in-place, then convert back to "persistent" before returning it.
He uses C# to provide examples of dynamic failure in compiler checked code. Since then, some of his 'what we want to write' pseudo-code examples are now reality in C#.
Do you have a link to the source for that? I'm curious to read.
If your description is accurate, Rust[1] is exactly what he wants. I've been learning Rust recently (with a little help from [2]) and it does a ton of static code analysis, is safe by default (no dangling or null pointers, no shared mutable state), uses Hindley-Milner type interface just like Haskell, and generally is what you would expect if Haskell and C++ had a baby.
Rust is pretty much exactly what a lot of people want and need; a high level language with a strong focus on safety and concurrency, while still allowing you to control the lower level details such as memory layout for good performance.
He sort of alluded to it, but there's the usual perceived tooling/ retraining/hiring / performance ceiling issues for shops with > N devs, N somewhere between 5 and 25 (I don't agree with his arguments, just repeating them)
Oh of course, it's far from trivial to switch to a new language. But if you're writing code you'll still be using in 10 years, the maintainability benefits of a safer language like Rust could be pretty huge. Also, Carmack switched from C to C++ not too long ago.
Has anyone here come across any particularly good books or other resources on functional style programming in C++? Especially ones that have been updated for C++11s new functional features.
"The major evolution that is still going on for me is towards a more functional programming style, which involves unlearning a lot of old habits, and backing away from some OOP directions."