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

This page summarizes the projects mentioned and recommended in the original post on news.ycombinator.com

Our great sponsors
  • WorkOS - The modern identity platform for B2B SaaS
  • InfluxDB - Power Real-Time Data Analytics at Scale
  • SaaSHub - Software Alternatives and Reviews
  • john-carmack-plan-archive

    Collection of John Carmack’s .plan files

    I was impressed by his concise daily planfiles.

    https://github.com/ESWAT/john-carmack-plan-archive/tree/mast...

  • Lombok

    Very spicy additions to the Java programming language.

    While you're not wrong RE: object references, Java does have the `final` keyword that you can apply to fields, local variables, method parameters, etc. This prevents the value from being re-assigned short of going out of your way to do reflection hacks and such. You can also apply it to classes themselves and prevent them from being inherited and modified that way.

    The only real 'gotcha' there is that, while it prevents whatever it is applied to from being reassigned, if the value is a mutable object of some sort (e.g., an array, mutable collection, etc), it, of course, does not prevent you from modifying the contents of the array, etc.

    It's been a while since I've written modern Java, but I want to say JDK 14 also brought records, which I believe are basically the equivalent of having `final` and `public` on everything as well as overriding the equality and hashcode functions on everything by default so that comparisons are done based on the contents of the object rather than the reference.

    And while I am sure many people are scared of the bytecode manipulation that it performs, Lombok [1] provides a lot of little utilities that make using immutable objects totally painless - e.g., applying `@Value` to the class generates all of the necessary boilerplate at compile time, or my favorite, `@With`, which generates methods for copying an immutable object with one of the fields changed (e.g., `val newUser = olduser.withName("Nayeon");`

    [1]: <https://projectlombok.org>

  • WorkOS

    The modern identity platform for B2B SaaS. The APIs are flexible and easy-to-use, supporting authentication, user identity, and complex enterprise features like SSO and SCIM provisioning.

  • language-ext

    C# functional language extensions - a base class library for functional programming

    > [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, 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...

  • fp-ts

    Functional programming in TypeScript

    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: https://github.com/gcanti/fp-ts/blob/master/docs/guides/pure...

  • boring-makefile

    An uninteresting makefile.

    I've been coding like that since at least 2016[1]. The trick is to make adding code generators as easy as adding source code. Not a game engine in my case, though there's a CppCon talk from 2014 which makes a passing reference to using code generators with C++ instead of templates for one.

    Assume source is src/.c and compiled to obj/.o, then introduce a third directory called gen. Change the makefile rule to copy .c from src to gen, and compile from gen to obj. Then add a makefile rule that says gen/foo.c can be built from src/foo.c.py by calling python on the source and redirecting stout.

    That means a C source file can be turned into a python program that generates the same source by wrapping it in a string literal, calling print and renaming the source file. No build system change. Then change the python as you see fit to work around the limitations of C.

    Works really well for a solo developer. Repo using this scheme is ~100 generated files (mostly lua, some python) vs ~600 C files. Plus ~20 C++ and one D, just getting started with that language.

    [1] https://github.com/JonChesterfield/boring-makefile/blob/mast...

  • HigherLogics.Algebra

    Numerical and algebraic abstractions for .NET

    Do it! Do it! Do it! If only for a couple of tests to see how much it changes the results. ;-)

    Unboxing sums is a nice optimization but then you can't naively use switch-patern matching to deconstruct them.

    I have a bunch of other things in Sasa and other libraries you might find useful. I'm not actively working on most of them anymore except for bug fixes. I learned a lot but didn't end up using a lot of these features as much as I'd hoped.

    For instance, being able to create open instance delegates in a way that automatically works around the CLR limits against such delegates to virtual methods. Some of the concurrency primitives are also interesting, as they implement an efficient atomic read/write protocols for arbitrary sized types using only volatile reads/writes (ie. avoid torn reads), and a sort of LLSC using only volatile read/write and a single interlocked inc/dec. Also, I added a kind system to CLR reflection to make working with it much easier [2].

    It seems we're thinking along the same lines for numeric types. I reproduced the Haskell numeric hierarchy [1], but I put that on hold because I was thinking a [Deriving] attribute would eliminate a lot of redundancy.

    Just FYI, clicking Num on the main GitHub markdown page doesn't jump to the link on the markup.

    Lots more to see if you're interested! I've played with parser combinators but never liked how they turned out, and settled on a simpler approach that was pretty interesting.

    [1] https://github.com/naasking/HigherLogics.Algebra

    [2] https://github.com/naasking/Dynamics.NET#kind-system

  • Dynamics.NET

    Extensions for runtime reflection and structural induction

    Do it! Do it! Do it! If only for a couple of tests to see how much it changes the results. ;-)

    Unboxing sums is a nice optimization but then you can't naively use switch-patern matching to deconstruct them.

    I have a bunch of other things in Sasa and other libraries you might find useful. I'm not actively working on most of them anymore except for bug fixes. I learned a lot but didn't end up using a lot of these features as much as I'd hoped.

    For instance, being able to create open instance delegates in a way that automatically works around the CLR limits against such delegates to virtual methods. Some of the concurrency primitives are also interesting, as they implement an efficient atomic read/write protocols for arbitrary sized types using only volatile reads/writes (ie. avoid torn reads), and a sort of LLSC using only volatile read/write and a single interlocked inc/dec. Also, I added a kind system to CLR reflection to make working with it much easier [2].

    It seems we're thinking along the same lines for numeric types. I reproduced the Haskell numeric hierarchy [1], but I put that on hold because I was thinking a [Deriving] attribute would eliminate a lot of redundancy.

    Just FYI, clicking Num on the main GitHub markdown page doesn't jump to the link on the markup.

    Lots more to see if you're interested! I've played with parser combinators but never liked how they turned out, and settled on a simpler approach that was pretty interesting.

    [1] https://github.com/naasking/HigherLogics.Algebra

    [2] https://github.com/naasking/Dynamics.NET#kind-system

  • InfluxDB

    Power Real-Time Data Analytics at Scale. Get real-time insights from all types of time series data with InfluxDB. Ingest, query, and analyze billions of data points in real-time with unbounded cardinality.

NOTE: The number of mentions on this list indicates mentions on common posts plus user suggested alternatives. Hence, a higher number means a more popular project.

Suggest a related project

Related posts