Live data from Hacker News

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

sevangelatos.com

141–150 of 179 posts

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

#141
post #28
post #27

Earlier quoted context omitted.

um, i seriously doubt that many const functions access global variables. if those in your code do, then i suggest your code has problems - the typical use of a const function is doing something like length() does on a std::string. how, or why. would such a function access a global variable?

I agree, but most would access internal state, which is equally impure. A pure function has no access to anything outside of its function parameters, and it changes nothing outside the scope of the function. In the context of a pure function, a local member variable is no different than a global variable because it’s outside the scope of the actual function. If you can’t take the function away from the class and have…

the instance of the class is a parameter to the function. As well your definition of pure is not the accepted definition. The definition depends absolutely nothing on the internal implementation as long as some in invariants hold.

1. For the given set of inputs ( including the attached object ) the output is always the same if called again with the same inputs ( including the attached object in the same state ).

2. No side effects. This means that nothing can be observed about the function apart from it's return value.

If you wish to quibble as to whether the object is a parameter or not then observe that in c++23 it will be allowed an explicit this parameter to methods This is not to make the methods more pure but to enable other types of optimizations and reduce duplication of code over const and non const methods.

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

#142
post #105

Earlier quoted context omitted.

Games and many similar domains are harder to write code for than CRUD. What you talk about works well for CRUD apps, but basically every set of development ideas works well for CRUD apps, it isn't hard to do.

As an ex-3D engine dev, I respectfully disagree. But if you're happy to give some examples of why you can't constrain your data-types then I'm happy to admit I'm wrong. I realise sometimes there's aspects of a game that needs to write to the metal , and compromises need to be made, but that isn't necessary for an entire game. I know people are using my language-ext library with Unity projects too, so I assume there a…

> [1] https://github.com/louthy/language-ext

Cool library. I've had a few of these patterns in my Sasa library for years, but you've taken it to the Haskell extreme! Probably further than most C# developers could stomach. ;-)

You might be interested in checking out the hash array mapped trie from Sasa [1]. It cleverly exploits the CLR's reified generics to unbox the trie at various levels which ends up saving quite a bit of space and indirections, so it performs almost on par with the mutable dictionary.

I had an earlier version that used an outer struct to ensure it's never null [2], similar to how your collections seem to work, but switched to classes to make it more idiomatic in C#.

I recently started sketching out a Haskell-like generic "Deriving" source generator, contrasted with your domain-specific piecemeal approach, ie. [Record], [Reader], etc. Did you ever try that approach?

[1] https://sourceforge.net/p/sasa/code/ci/default/tree/Sasa.Col...

[2] https://sourceforge.net/p/sasa/code/ci/57417faec5ed442224a0f...

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

#143

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

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.

It's not just the ADTs but the exhaustive pattern matching that help make sure you've covered all of the cases in your ADT. You'll see JavaScripters use objects as a poor man's ADT where they'll use the key name as a the constructor or a { tag: key, ... }, but that has caveats (aside from feeling less first-class): 1) JavaScript can't do exhaustive pattern matching so you'll always need to handle null cases, 2) checks must all happen at runtime which means you'll end up having to throw/catch exceptions and nothing will hold your hand to say that you've missed a case. TypeScripters can handle 1 & 2 for safety, but the ergonomics are bad and verbose so you'll see a lot of folks skip it. Similar languages will have pattern matching, safety, but lack the lightweight/dense ergonomics. When you dive into a language in the ML family (Haskell, OCaml, Standard ML, et. al.), the ergonomics make you want to use them, and they are idiomatic to the ecosystem and you'll want to use them for just about everything--either the ones in the Preludes/stdlib like Maybe, Either, List, etc. or by building them yourself.

An example in the wild that demonstrates this `fp-ts`'s explanation of how to do ADTs in TypeScript where you can see the comparison in PureScript is a one-liner (both data and polymorphic data): https://github.com/gcanti/fp-ts/blob/master/docs/guides/pure...

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

#144
post #28

Earlier quoted context omitted.

I agree, but most would access internal state, which is equally impure. A pure function has no access to anything outside of its function parameters, and it changes nothing outside the scope of the function. In the context of a pure function, a local member variable is no different than a global variable because it’s outside the scope of the actual function. If you can’t take the function away from the class and have…

the instance of the class is a parameter to the function. As well your definition of pure is not the accepted definition. The definition depends absolutely nothing on the internal implementation as long as some in invariants hold. 1. For the given set of inputs ( including the attached object ) the output is always the same if called again with the same inputs ( including the attached object in the same state ). 2. N…

That's an odd interpretation of purity. As per the article:

> A pure function only looks at the parameters passed in to it, and all it does is return one or more computed values based on the parameters... It doesn’t maintain internal state... Ideally, it isn’t passed any extraneous data

If you send in an entire object state as an argument (or as an attached instance, as you argue), this is not a quintessence of functional purity and strays far from these concepts. Consider: your object can have all sorts of data in it that you are passing to every member function, and it is unlikely all that state needs to be used in every function -- as the article points out, this defeats the purpose as it's akin to passing in a big pointer to a bunch of state.

In a pure function, it accepts only the specific data it needs, nothing else or extraneous, and returns a value based on these specific inputs.

As I mentioned in another comment, you could say for a particular type that since it only has one data member, no harm done -- but in the general sense, most objects have more than one piece of data, and not every function needs every piece of data, so passing all that in is a sloppy approach to purity.

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

#145
post #8

He talked about it a bit during his lex friedman interview. He said he spent some time there, IIRC, and found it somewhat intriguing, perhaps interesting, but needed to get shit done eventually and so stopped his investigation. Very pragmatic. From what I can see in this article he mostly seemed to like the purity aspect of FP. It seems to me like a number of FP concepts aren’t really “FP things”, as Carmack points o…

>He said he spent some time there, IIRC, and found it somewhat intriguing, perhaps interesting, but needed to get shit done eventually and so […] I think this resonates with many. My take is: don’t be dogmatic. Regardless of the language, one should write as beautiful, composable and pure as possible - but no more than that. Where only performance matters, oop wins. When clarity is the only priority, Haskell wins. Bu…

> Fundamentalists rarely make policy with the nuance reality deserves.

Perhaps "the other guys" aren't really fundamentalists and we just like a good old ad-hominem every now and then.

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

#146
post #144

Earlier quoted context omitted.

the instance of the class is a parameter to the function. As well your definition of pure is not the accepted definition. The definition depends absolutely nothing on the internal implementation as long as some in invariants hold. 1. For the given set of inputs ( including the attached object ) the output is always the same if called again with the same inputs ( including the attached object in the same state ). 2. N…

That's an odd interpretation of purity. As per the article: > A pure function only looks at the parameters passed in to it, and all it does is return one or more computed values based on the parameters... It doesn’t maintain internal state... Ideally, it isn’t passed any extraneous data If you send in an entire object state as an argument (or as an attached instance, as you argue), this is not a quintessence of funct…

The object state is a parameter passed to the function. The number of arguments passed to a function does not affect it's purity. The feed forward recognition phase of an LLM is pure but has billions of parameters. The purity is defined by it's behavior in relation to again.

1. Does it always return the same result for the same input parameters 2. Does it modify anything other than return a value

These two properties define purity. Simple and not odd at all.

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

#147
post #126

The psychological aspect of 'purity' in this concept is as interesting as anything. I think that devs are naturally prone to be obsessive compulsive about such things. We can see 'defeating the borrow checker' as a 1st order objective. I think devs fee that 'defeating the borrow checker' gives us much more satisfactiong than 'fulfilling the customers needs'. There's something a big 'chinse finger trap' about FP that…

On the other hand, there are those who are too stubborn to use anything but a dull knife and a broken hammer. There are situations where you can, e.g., make an error state impossible by defining a couple of types, or using one or two lines of generics. But then, what should have been a simple change becomes an ordeal because the other person lacks the background on all of these ideas.

I think all CS curricula should have some sort of class that explores languages like Haskell and Rust, the problems they attempt to solve and how they go about solving them, if only for the intellectual experience. What language you end up using, and what ideas you end up applying, is then up to you on a case-by-case basis.

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

#148
post #124

Earlier quoted context omitted.

Well, you say that works, but basically nobody who programs creatively writes code that way. People who like functional programming mostly writes compilers or similar processing throughput systems, you see it a ton there, but you barely see it on the other side of the programming landscape such as game programming. So, you say it works for you, but basically every example of the things I talked about are done in a ve…

The biggest mistake you make is thinking you do something different to me or any other software engineer. You don't. You do the same thing and the same rules apply. You seem to think you're the only person doing anything creative. Well here's a newsflash: it's the same process in games as it is in all other forms of software development. The end result might look different, but the process is the same. The best devs…

>>That is possibly why there hasn't been a big push for FP in games yet, because they haven't yet hit the complexity limit of OO like others have.

Are you saying the biggest and probably most complex software (OS-es, browsers, etc.) out there is written in fp languages?

>> Games is one of the easiest sectors to be in, the complexity level is low compared to other sectors, especially now all the difficult bits are done for you (off the shelf engines).

As far as I know those "off the shelf engines" are not written in fp languages. And I would say they are complex.

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

#149
post #99

Earlier quoted context omitted.

And how do you know you've constricted your data enough? You don't and you get bugs. You don't have a perfect specification you are building. In the micro scale in terms of variables this is not the biggest problem that exists with programs. Enums don't somehow become garbage because it's technically possible to overwrite them with garage. The macro state of the entire system adds much more complexity and bugs. There…

> And how do you know you've constricted your data enough? Types are composable. Sum-types and product-types allow the composition of smaller types into larger ones. They come in the form of discriminated-unions and records in FP languages. So when it comes to the question of how do I know when I've constricted my data enough, it's when I know all the components of all types have been constricted enough. You don't ha…

>I know when I've constricted my data enough, it's when I know all the components of all types have been constricted enough.

Do you not see that this is a tautology?

>That's our job, to translate the requirements into code, and the logic is that bit and therefore more prone to human error.

The requirements given are typically ambiguous and it is our job to come up with the complete specification ourself. Our specification may have bugs ignoring the implementation. I am not denying that with enough diligence you can mostly avoid errors in implementing the spec, but I am saying finding the correct specification for what your code should do is too hard to do compared to implementing a wrong spec and then fixing it as bugs come up.

>Those using languages like Haskell don't have the inheritance problem. Everything is sum-types, product-types, or exponential-types (functions) and is therefore composable.

Inheritance is just a different way to create sum types.

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

#150
post #144

Earlier quoted context omitted.

That's an odd interpretation of purity. As per the article: > A pure function only looks at the parameters passed in to it, and all it does is return one or more computed values based on the parameters... It doesn’t maintain internal state... Ideally, it isn’t passed any extraneous data If you send in an entire object state as an argument (or as an attached instance, as you argue), this is not a quintessence of funct…

The object state is a parameter passed to the function. The number of arguments passed to a function does not affect it's purity. The feed forward recognition phase of an LLM is pure but has billions of parameters. The purity is defined by it's behavior in relation to again. 1. Does it always return the same result for the same input parameters 2. Does it modify anything other than return a value These two properties…

Not sure why you are arguing about this? The issue is about the idiomatic, expected style that surrounds pure functions and functional programming. The posted article goes into this in greater detail (and more elegantly than this thread) as to why the pattern you describe -- passing a big pointer of potentially large composite data to a function (implicitly or otherwise) -- is not in the spirit of FP purity. If you want to be pedantic, you are welcome and you are academically correct, but the tradition of FP in practice is not to do this (as also pointed out in the article). It's not a question how many arguments but rather if there is extraneous data that the function does not need, as would be common with the full object.
Post reply on HN