1,327 karma · joined December 13, 2017
Take the discrete Fourier transform for example, whose domain group is Z/nZ: the integers-mod-n for some fixed n. The basis functions for the transforms need to be group homomorphisms f: Z/nZ -> C, meaning that they need to satisfy f(a + b) = f(a) f(b) for all a, b. Without too much trouble you can work out that the only such functions are of the form f(k) = exp(2 pi i m k / n) for m = 0, ..., n - 1. So once the group structure is remembered, the choice of basis is forced. The only ambiguity is relabelling the basis elements back into Z/nZ, which is a (very useful) hack.
The same reasoning (with more complicated working) applies to taking the Fourier series of a periodic function, or taking the Fourier transform of a continuous function on the real line. There is a canonical choice of basis, but some ambiguity in what labels to assign to those basis elements.
That’s a very poor outlook on an incredibly useful mathematical tool. Maths is done essentially by finding new ways of viewing an old thing, and restricting the set of tools available seems like a poor idea in general. As for teaching mathematics, finding more ways of looking at something, however imprecise, is a huge aid to understanding. Never be afraid to show someone a picture that’s only 80% true but could potentially aid their understanding in how something works!
I also disagree that the “circular paths” visualisation is misleading - what is misleading about it? This is a completely accurate representation of how a Fourier series is added up to get a real part which varies over time. It even makes it intuitive that multiplying the series by a fixed complex number (corresponding to rotating and scaling the “orbiting” points on the left) will have the effect of translating the resulting signal to the right or the left.
Also for what it’s worth, the product of complex numbers is easy to visualise? Add their angles, multiply their magnitudes.
I think unsignedness on array indices is one of those places where the “make invalid states unrepresentable” mantra has gone too far: yes it’s nice that theoretically the whole 64-bit index space is addressable from a byte array based at 0, but in reality -1 is just as stupid and invalid an array index as 2^64 - 1 for pretty much every use-case. What we gain is negligible; what we lose is a lot of sensible code for dealing with index arithmetic.
I guess it’s a matter of interpretation: does “x == 0” mean that we are comparing x with the integer zero, or the “zero value” of the same type as x? Numeric code is difficult for many reasons, but something that can make it much more tractable is having the code as close as possible to the mathematics underlying the algorithms, and this kind of “polymorphic constant” behaviour can help a lot, especially for integer literals which unambiguously embed into essentially any numeric type you could think of.
By contrast CS offers nothing like this - movement is always strictly along the ground/ladders/stairs etc, and gunfights are more like who saw who first. It just feels boring to play in comparison - sure the tactics are interesting, but there’s nothing like the feeling of freedom you get from some other games.
I’d like there to be more of a focus on games that are fun to play, in terms of movement and combat mechanics just feeling awesome. Horizon Zero Dawn is another great example of making combat awesome.
If you tend to write code once, and come back to it months or years later, breakage is a huge issue - why is the code that was working perfectly well in 2019 broken today? Why do I have to now fix 4 dependencies in order to do what I actually wanted to do (make some small change). This is the reason that I tend to write all my own code in TypeScript projects, but for my Clojure/ClojureScript projects I am much more happy to pull in the occasional library.
I would say that the “don’t break things” culture is less about static vs dynamic types, and more about the style of software being written and the maintenance expectations.
I’m sure there are some good ways around this problem, and I would love to have proper tail calls available in most languages.
If the capacity is only increased by some constant each time, rather than multiplied, this leads to O(n^2) running time for a sequence of n append-to-back operations, surely something to be avoided.
I found similarly "meh" performance improvements when trying to port my Javascript code to webassembly: either modern Javascript implementations are absolutely amazing, or webassembly runtimes still have a ways to go.
Whether moving an object between two GC’d heaps is even possible (let alone simple or complicated) surely depends a lot on the GC implementation. The implementation of concurrent GCs is not easy...
The author is saying that because pictures cannot easily capture arbitrary sentences, text is better. But the same applies with the roles reversed: text cannot capture arbitrary pictures! Instead of saying that one is better than the other outright, let’s move to media (for reading, composing, and programming) where the two can be intermingled appropriately, with as little friction as possible. We certainly have the technology for it...
Of course for efficiency concerns, programmers should have access to fixed-width integers as well, but I think that true integers should be the default. It was also contemplated for Go [1], and was pointed out that true integers benefit from being a core language feature, since the compiler can perform a lot of SMI (SMall Integer) optimisations, which essentially use machine integers for small enough integers, before switching to a more complicated implementation. They also recognise that the biggest benefit to a bigint type is making it the default integer type.
So I think it is a continuum, and really fantastic mathematics will feature ideas from all the way along the spectrum: “discoveries” for the beauty, “inventions” for the problem solving, and the “in between” for the subtlety and art.
Law-abiding means that some equation holds, for example a monoid instance should be associative, so ((a <> b) <> c) should always produce the same result as (a <> (b <> c)). There is nothing checking that my implementation of <> abides by this law, but other programmers (and optimisers or compilers, maybe?) can make use of it, and might write incorrect code if my instance does not abide by the law.
Another example would be functors: it should be the case that (fmap f) . (fmap g) is the same as fmap (f . g), for all functions f and g that make sense in the equation (output type of g must match input type of f).