HNHacker News
TopNewBestAskShowJobs

matt-noonan

166 karma · joined March 9, 2017

submissionscomments
matt-noonan··on A couple million lines of Haskell: Production engineering at Mercury
This is really a non-issue in practice. In the olden days, you’d just make the change and then spend a pleasant hour or two fixing the callsites by responding to compiler errors in a pretty low-thought, mechanistic way. Nowadays you can outsource that part to a coding agent and get on with your life.

In any case, the fact that the compiler knows what code needs to be updated is a real superpower.

matt-noonan··on A couple million lines of Haskell: Production engineering at Mercury
As somebody who has helped hire many Haskell devs, I can say that lots of Haskell experience isn't always a positive. We have to filter carefully to make sure that we end up with developers who want to build real things, not developers who just want to get paid for noodling around with Haskell. As far as I'm concerned, I'd much rather hire somebody with lots of experience building things who ended up coming to Haskell later because they viscerally understand the benefits and risks. Somebody with lots and lots of Haskell experience who never delivered much is a big risk.
matt-noonan··on ArXiv Declares Independence from Cornell
This is particularly funny because arXiv doesn't just predate Web 2.0, it nearly predates the public web entirely (only missing it by about two weeks)
matt-noonan··on Category Theory in Programming
I just realized I botched the description of Brzozowski's algorithm, step (b) should be "determinize the NFA using the powerset construction". Mea culpa.
matt-noonan··on Category Theory in Programming
There is a very useful perspective in which categories are just typed monoids, and the monoid operation can only be applied when the types "line up". For example, here are some useful operations which do not form a monoid:

- PUSH(n) where n is a floating-point number

- POP

- CLEAR

- ADD, SUB, MUL, DIV

- ID

These can be interpreted as operations on a stack of floating-point numbers in the obvious way, PUSH(1.2) * [3.14] == [1.2, 3.14], POP * [1, 2, 3] == [2, 3], ADD * [1, 2, 5] == [3, 5], CLEAR * [1, 2, 3] == [], ID * [1, 2, 3] == [1, 2, 3], etc. However, not all of the compositions of stack operations are legal. For example, ADD * PUSH(1) * PUSH(2) is fine and equivalent to PUSH(3), but ADD * PUSH(1) * CLEAR is illegal.

Ok, so our stack operations don't form a monoid. But they obviously can still be composed, sometimes, so what do we have if not a monoid? They form a category! There is one object for each natural number, representing the height of the stack. So there are arrows like PUSH(3.14) : Height_{n} -> Height_{n+1} for all n, and POP : Height_{n} -> Height_{n-1} whenever n >= 1, and ADD : Height_{n} -> Height_{n-2} whenever n >= 2.

Another common example is matrices. Square matrices form a monoid, but what about arbitrary rectangular matrices? They don't form a monoid, but they do form a category where the objects are natural numbers, and the arrows N -> M are just the MxN matrices. You can't multiply any two matrices, but if you have a P -> Q matrix (QxP) and a Q -> R (RxQ) matrix then you can multiply them to get a P -> R matrix (RxP).

matt-noonan··on Category Theory in Programming
Yes, there are a number of them. Here are some examples off the top of my head:

- Moggi was studying the problem of equivalence of programs, and noted that the traditional approach to modeling a program as a total function Input -> Output is problematic. He pioneered the use of monads and Kleisli categories as a foundation for reasoning about equivalence of real programs, including all the real-world nastiness like non-termination, partiality (e.g. throwing an exception that kills the program), non-determinism, and so on. https://person.dibris.unige.it/moggi-eugenio/ftp/lics89.pdf

- Linear logic (and it's close relative affine logic) was the inspiration behind Rust's ownership model, from what I understand. Linear logic was originally described in terms of the sequent calculus by Girard (http://girard.perso.math.cnrs.fr/linear.pdf), but later work used certain categories as a model of linear logic (https://ncatlab.org/nlab/files/SeelyLinearLogic.pdf). This answered and clarified a number of questions stemming from Girard's original work.

- Cartesian-closed categories (CCCs) form models of the simply-typed lambda calculus, in the sense that any lambda term can be interpreted as a value in a CCC. Conal Elliott pointed out that this means that a lambda term doesn't have just one natural meaning; it can be given a multitude of meanings by interpreting the same term in different CCCs. He shows how to use this idea to "interpret" a program into a circuit that implements the program. http://conal.net/papers/compiling-to-categories/

- Mokhov, Mitchell, and Jones studied the similarities and differences between real-world build systems and explained them using different kinds of categories. https://www.microsoft.com/en-us/research/uploads/prod/2018/0...

- There is a classical construction about minimizing a DFA due to Brzozowski which is a bit magical. Given a DFA, do the following process twice: (a) get an NFA for the reverse language by reversing all edges in the DFA and swapping start / accept nodes, then (b) drop any nodes which are not reachable from a start node in the NFA. The result will be the minimal DFA that accepts the same language as your original DFA! Bonchi, Bonsangue, Rutten, and Silva analyzed Brzozowski's algorithm from a categorical perspective, which allowed them to give a very clear explanation of why it works along with a novel generalization of Brzozowski's algorithm to other kinds of automata. https://alexandrasilva.org/files/RechabilityObservability.pd...

- I would also put the development of lenses in this list, but they haven't leaked very far outside of the Haskell universe yet so I don't think they are a compelling example. Check back in 5 years perhaps. Here's a blog post describing how lenses relate to jq and xpath: https://chrispenner.ca/posts/traversal-systems

- I've personally had success in finding useful generalizations of existing algorithms by finding a monoid in the algorithm and replacing it with a category, using the fact that categories are like "many-kinded monoids" in some sense. I haven't written any of these cases up yet, so check back in 2 years or so. In any case, they've been useful enough to drive some unique user-facing features.

matt-noonan··on Galois Theory
That is definitely not what Galois Fields are about.
matt-noonan··on Ask HN: What resources do you recommend for learning Haskell?
No, it's quite different. A super rough but somewhat accurate starting point is to think of a lens as being like the `.foo.bar.baz` in `myobject.foo.bar.baz`: a way to describe some "location" inside of a data structure, in a way that allows for getting and setting. But lenses are also data, so they can be stored in variables, manipulated, and so on. It's a very useful concept, and very much not required or helpful when learning Haskell. It's also not a core Haskell concept in any sense, just something that has been built on top of Haskell as a library.
matt-noonan··on Ask HN: What resources do you recommend for learning Haskell?
A common failure mode is for people to think Haskell is some special snowflake that requires reading 50 books and papers to understand. It doesn’t. Learning by doing is definitely the way to go. LYAH is fine but not great at practical problems. Real World Haskell is somewhat out of date but better at the actual “how do I make a program that does real things?” question. Best bet is to hack until you get stuck or your solution seems too ugly, then ask for leads on Reddit or the FP discord.
matt-noonan··on Memory-mapped IO registers in zig (2021)
There are some interesting solutions out there, such as bit-banding used in some ARM Cortex CPUs. This maps entire bytes in the high part of the address space to single bits in the low part of the address space, so that you can make an atomic set or clear of a single physical bit by writing to a byte of memory. https://spin.atomicobject.com/bit-banding/
matt-noonan··on The quest to decode the Mandelbrot set
It's almost correct, but misses the point in an annoying way that kind of ruins the example. What does work is something like the subset of the plane given by { (x, y) | x real, y rational } U { (0, y) | y real }. This is connected, because you can walk from any point (x,y) to any other point (x',y') by traveling horizontally to the Y axis at (0,y), vertically to (0,y'), then horizontally to (x',y'). But it isn't locally connected away from the Y axis because for a tiny enough open set S around a point (x,y), there are other points in S that you can't get to from (x,y) without leaving S.
matt-noonan··on The Derivative of a Regular Type is its Type of One-Hole Contexts (2001) [pdf]
It gives a set-theoretic bijection, but not one that plays by all the rules I mentioned above. In particular, you don't get a bijection that corresponds to a finite, non-looping program.

Generally speaking, Cantor–Schröder–Bernstein does not give you a (finite) construction of the bijection, because you have to follow the inverses of f and g back until you either end up with something from the first set with no preimage under g, or something from the second set with no preimage under f, or you find that you are iterating forever. You have to make a decision based on whether or not a certain computation terminates. That's essentially why it is a non-constructive theorem (in fact, it even implies the law of excluded middle)

But in your specific case, we're kind of in luck. The injections are simple enough that you can work out the bijection that you'd get from König's proof manually, and it goes like this:

Concretely, let's write (AB) for the tree with left subtree A and right subtree B, and write . for the empty tree. Your injections are f(A,B) = (AB) and g(T) = (T.). Alternately applying f/g partitions the set of trees into a bunch of infinite sequences. Your bijection is given by: if T is part of the sequence that begins with the empty tree, pair T with (T,.). If T is part of some other sequence, then T is not the empty tree; it is of the form T = (LR), and you should pair T with (L,R). Concretely, the sequence that starts with the empty tree looks like:

. -> . . -> (..) -> (..) . -> ((..).) -> ((..).) . -> (((..).).) -> **

In other words, the single trees that appear in the empty list's sequence are the fully-left-leaning trees like ((((..).).).); all other trees are in the other sequences. So to decide where your tree goes in the bijection, you have to do this:

if (T is fully left-leaning) then (T,.) else (left-child(T), right-child(T))

And computing whether or not T is fully left-leaning involves an unbounded amount of computation. You have to actually walk the whole tree. So this bijection won't correspond to a finite, non-looping program. In a sense, the algorithm you get from Cantor–Schröder–Bernstein is not "continuous", but the one you get from the Seven Trees In One construction is.

matt-noonan··on The Derivative of a Regular Type is its Type of One-Hole Contexts (2001) [pdf]
It's definitely surprising, for a couple of reasons: 1. It isn't just the uninteresting result that the set of trees has the same cardinality as the set of 7-tuples of trees; the bijection here is given by a finite, non-looping program built out of `isEmpty : Tree -> Bool`, `getLeft : Tree -> Maybe Tree`, `getRight : Tree -> Maybe Tree`, and the constructors `empty : Tree` and `join : Tree x Tree -> Tree` 2. It isn't true for any number 1 < x < 7 3. In any case, why should it work out exactly? Why not "one tree can be encoded into seven trees, or one of these 13 remaining cases"?

The paper is quite good, but Dan Piponi has a great blog post that recasts the isomorphism as a game of "nuclear pennies", which is a fun puzzle to work out yourself: http://blog.sigfpe.com/2007/09/arboreal-isomorphisms-from-nu...

matt-noonan··on Algebraic Geometry for Computer Graphics
Here are a few off the top of my head, as a mathematician-turned-programmer who never has been an algebraic geometer.

- Elliptic curve cryptography (https://en.wikipedia.org/wiki/Elliptic-curve_cryptography)

- Grobner bases, with many applications. Example domains: coding theory, robotics, signal processing... (https://math.stackexchange.com/questions/32421/applications-...)

- Physics [solitons] (https://kasmana.people.cofc.edu/SOLITONPICS/)

- Physics [string theory] (https://royalsociety.org/~/media/people/new-fellows-2014/Pre...)

- Automata theory, via "tropical" algebraic geometry (https://link.springer.com/article/10.1007/s00233-019-09999-8)

This is not even considering applications of AG to other areas of pure mathematics, which are extensive.

matt-noonan··on Ask HN: Best beginner friendly linear algebra book?
Another vote here for the matrixanalysis.com book. It is a really excellent book and takes a reasonably pragmatic approach to linear algebra. Coming from a programming background, you're more likely to find some things that resonate with you here. For example, this is one of the few introductory linear algebra books that deals with sensitivity analysis, which is useful to think about when dealing with floating point arithmetic instead of real arithmetic.
matt-noonan··on What is the inverse of a vector?
Yes, I meant that equation to be interpreted in the GA used in the article. But essentially all geometric algebras also have zero divisors, for similar reasons.
matt-noonan··on What is the inverse of a vector?
> it is deficient in various ways when compared to [...] differential forms (e.g. if you want to work basis-free)

There is nothing basis-dependent in Geometric Algebra. This presentation started from a basis, but then again so do many presentations of differential forms, leading to 2-forms like dx \wedge dy and so on.

The actual difference is that Geometric Algebra requires a choice of inner product (actually, you can get away with any bilinear form), while differential forms do not. However, some of the important operations on differential forms in physics do require an inner product (e.g. the hodge star operator and the codifferential), so you end up back on equal footing with GA again.

matt-noonan··on What is the inverse of a vector?
No, this is wrong. Geometric algebras aren't division algebras in general: they usually have zero divisors. Objects that live in a single grade are invertible, but composite objects don't always have multiplicative inverses.

As a concrete example, consider the elements 1 + x and 1 - x. Their product is 1 + x - x - xx = 1 + x - x - 1 = 0. So certainly 1 + x doesn't have an inverse, either.

matt-noonan··on Functors, Applicatives, and Monads in Pictures (2013)
A more accurate translation to food would be something like “does it feel weird to call a physical plate of spaghetti a recipe?”
matt-noonan··on Conterintuitive facts in mathematics, CS, and physics
Inside the model, “the reals are uncountable” means you have two sets R and N, and there is no surjective function from N onto R. That function would be a set as well; a certain subset F of NxR, say. But even if we can externally enumerate R, there is no reason to expect that our external enumeration corresponds to a set F that exists in the model.
matt-noonan··on Elementary Calculus: An Infinitesimal Approach (2000)
I learned it originally from Jim Henle, and iirc he had a textbook on the hyperreals (“Infinitessimal Analysis”, possibly?)

This honors project has what looks like an accurate write up of the construction along with proofs of some of the main theorems: https://ideaexchange.uakron.edu/cgi/viewcontent.cgi?article=...

matt-noonan··on Elementary Calculus: An Infinitesimal Approach (2000)
Although the original statement about “infinitesimals being functions that vanish at 0” was stated with confidence, it is wrong.

The usual construction of the hyperreals replaces real numbers with sequences of real numbers, and also introduces a nontrivial equivalence relation on the sequences, making two sequences equivalent if they agree on a “large” set of terms. The real numbers get represented by the constant sequences, infinitesimals get represented by sequences that approach 0, and infinite numbers are represented by sequences that grow without bound.

The magic is in how “large set of terms” is defined. You need a “large set” relation with the property that finite sets are not large, and for any set either the set or its complement is large. Then we can resolve your question: say you had two not-always-zero sequences that multiply to give the all-zero sequence. Then the set of zero positions is large for one of those two sequences. And that means one of your sequences is equivalent to the zero sequence. The field axioms are saved!

matt-noonan··on Parsix: Parse Don't Validate
The principle was certainly known, but I think Alexis really does deserve the credit for the catchy "parse, don't validate" wording. A Google search for that phrase, restricted to October 2019 and earlier, has no results (or rather, the results that do show up all are more recent additions such as comments, appended to previously-existing content)
matt-noonan··on The visitor pattern is essentially the same thing as Church encoding
This is exactly right. The relevant quote from the article is this:

> The reason we care about Church-encoding is because not all programming languages natively support sum types or recursion (although most programming languages support product types in the form of records / structs).

> However, most programming languages do support functions, so if we have functions then we can use them as a “backdoor” to introduce support for sum types or recursion into our language. This is the essence of the visitor pattern: using functions to Church-encode sum types or recursion into a language that does not natively support sum types or recursion.

matt-noonan··on The Evolution of a Haskell Programmer (2001)
This is good advice. It seems like many people get stuck in a rut of thinking they need to study, study, study before they will be productive. That's backwards. Start building things right away; you'll immediately perceive what you need to understand next, and why.
matt-noonan··on Generalizing 'jq' and Traversal Systems using optics and standard monads
The point is really that lenses are values that represent locations in a data structure. And, as values, they can be combined, transformed, serialized, etc etc. Imagine having a type that represents a chain of method selectors, and that gives you some idea of the purpose.

The fact that method selectors only appear very rarely as first-class values in most languages means that most people aren’t tuned in to scenarios where they could be applied. But I bet you’ve invented special cases of this yourself, when you had a function that needed to dig data out of one of several locations, depending on other inputs.

matt-noonan··on Algebra Driven Design
I'm pretty sure you're reading this line wrong (though I don't blame you; the wording could probably be improved here). But I believe what the author is saying is "you can use these techniques in any language, but if you aren't using Haskell they might be more annoying to implement or maintain".
matt-noonan··on Algebra Driven Design
Just to be totally explicit here, there are no FP languages that require learning category theory. There is no reason to conflate [a small subset of] research on programming languages with the practice of using those languages day-to-day to Actually Get Shit Done.
matt-noonan··on Ramanujan Surprises Again (2015)
David Kelly [1] once told me that as a grad student at Princeton, he somehow managed to get the Sunday New York Times delivered to him late Saturday night. He'd stay up all night solving the crossword puzzle, then dazzle everybody who was stumped on it the next day.

[1] https://www.vinc17.net/yp17/index.en.html

matt-noonan··on A Pythonista's Review of Haskell
And the only reason the language pragma is deprecated is because TypeInType is now just how things are, by default, with no opt-out.
Page 1 of 2Next →