HNHacker News
TopNewBestAskShowJobs

joppy

1,327 karma · joined December 13, 2017

submissionscomments
joppy··on Noisy appliances: How loud is your house?
I recently bought a clothes dryer and took it back because of the noise (which raised a lot of eyebrows - “of course dryers make noise”). Swapped it for a different brand at about the same price point and the difference in noise is night and day, this new one is so much quieter I can’t even hear it if the laundry door is closed. Compared to the first one which was uncomfortably audible throughout the whole house...
joppy··on What is the Fourth Dimension? (1884)
The whole point of special/general relativity (at least the Minkowski approach to it, ie the approach that calls time a dimension) is that it the time dimension is related to the spatial dimensions. At non-relativistic speeds it seems unrelated, but for very fast-moving objects or particles the interplay between space and time becomes quite important, and for an object moving along a path the notions of time as-measured-by an external observer (roughly, ending coordinate in the time dimension minus the starting coordinate) and time as-measured-by the object (roughly, the Minkowski-length of a path in spacetime) become quite different things.
joppy··on Neovim 0.5 is overpowering
It is pretty common nowadays in functional programming languages to find operators which reverse the order of application or composition, so rather than writing f(g(h(x))) one can instead write “x |> h |> g |> f”. This style is quite popular, which leads me to believe that many people prefer object-action over action-object in same way. (Although perhaps this is more a critique of action-object-object vs object-action-action)
joppy··on An Interactive Guide to the Fourier Transform (2012)
If you add some additional structure of remembering that the domain of these transformations is an abelian group, then there is actually a completely canonical version of the Fourier transform, whose domain is the dual group - the only non-canonical part is picking labels for elements of the dual group, but there is no other choice of basis functions. (Wikipedia gets a little into this, you can find it under "Pontryagin duality" on the Fourier transform page, but it's not really enough of an explanation to learn from).

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.

joppy··on An Interactive Guide to the Fourier Transform (2012)
> The complex plane is a crutch that gives you pretty pictures.

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.

joppy··on An intro to Zig's integer casting for C programmers
The Rust approach of having all indexing being unsigned has been extremely annoying for programming algorithms which need to do interesting indexing patterns like walking backwards through an array while indexing into another: the fact that “i >= 0” can no longer be used as a loop condition is quite exasperating. It means other more complicated indexing or looping approaches need to be used, and when showing code to coworkers (mostly mathematicians), they puzzle over this for a while before asking “why not just use i >= 0”? It’s not just this case - doing index arithmetic in general is vastly complicated by the fact that it’s difficult to detect idx < 0.

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.

joppy··on An intro to Zig's integer casting for C programmers
The number 0 happily and unambiguously plays a role in many numeric types and algebraic structures, and it’s kind of nice if the compiler can just figure out the type that it should be from context. Comparing a literal 0 to a double should cast to a double, comparing a literal 0 to an int should cast to an int, etc. I would be happy for “(int) 0 == (double) 0” to raise a type error though.

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.

joppy··on The Byte Order Fiasco
Why is it an issue any more than say, order of fields in a struct is an issue? In one case you read bytes off the disk by doing ((b[0] << 8) | b[1]) (or equivalent), with the order reversed the other way around. Any application-level (say, not a compiler, debugger, etc) program should not even need to know the native byte order, it should only need to know the encoding that the file it’s trying to read used.
joppy··on Blizzard has lost almost 29% of its overall active playerbase in three years
On the other hand, one of the things I like so much about Overwatch and TF2 is the importance of mobility and crazy movement patterns - with some characters you can be flying around the map, ending up in odd places, and characters usually have enough HP to be in a good fight with someone when they land.

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.

joppy··on Six years of professional Clojure development
If you constantly work on the same code base day-in, day-out, breakage through the language or libraries is much less of an issue. You can keep up with current developments in the language and/or libraries, and update as necessary. Incremental changes are not a big deal, since you’re always around when they happen.

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.

joppy··on Introduction to Pluto.jl
Pluto is quite fast for me - could you perhaps be hitting the first-run JIT startup time in Julia? Do the cells re-evaluate quickly, after whatever code they depend on has been JITted?
joppy··on Proposed futex2 allows Linux to mimic the NT kernel for better Wine performance
I don’t think this is an achievable goal for a kernel, since it has strictly more permissions than user code. There are some parts of user code that simply cannot be made faster by changing how they’re written: no amount of rewriting in Rust/C/C++ will help if the relevant kernel primitives are not there. The kernel must move with the times, and incorporate new features as they are (tastefully) decided upon.
joppy··on Parsing Protobuf at 2+GB/S: How I Learned to Love Tail Calls in C
One reason that this is not done by default is that it can make debugging a surprise: since each function does not leave a stack frame, stack traces are much less useful. This is not so bad in the case of a tail-recursive function which calls itself, but if proper tail calls are done between multiple functions, you could have A call B tail call C tail call D, and if D crashes the stack trace is just (D called from A).

I’m sure there are some good ways around this problem, and I would love to have proper tail calls available in most languages.

joppy··on The Boyer-Moore Fast String Searching Algorithm
Straightforward Boyer-Moore is worst case O(nm), but (I believe) there are modifications that make it linear time. There are other string searching algorithms like Knuth-Morris-Pratt which are linear in the size of the two inputs. My point was that non-naive string searching algorithms absolutely do matter, and that no amount of cache-efficiency is going to save an implementation from hitting the quadratic behaviour intrinsic to a naive algorithm.
joppy··on The Boyer-Moore Fast String Searching Algorithm
String searching is interesting in that the naive algorithm is quadratic-ish: imagine searching for “aaaab” in the string “aaaaaaaaaaaa” by comparing the characters 1-5, then 2-6, and so on. So at least some kind of clever insight needs to be used so as to not hit quadratic behaviour - some mechanical way of deciding that a chunk of the input can be skipped after being read. The value of extra skips (not examining some of the input) is debatable, but these algorithms are still very much relevant when wanting to avoid accidentally-quadratic behaviour.
joppy··on Is WebAssembly magic performance pixie dust?
In order for append-to-back to have O(1) amortised running time, the capacity needs to be multiplied by some constant >1. Any constant would do just fine in terms of complexity, but 2 is the obvious simple choice, being the first integer greater than 1.

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.

joppy··on Is WebAssembly magic performance pixie dust?
Yes, I did think it wasn't a good way to used typed arrays, but I was still surprised that when allocating only a very small number of large arrays, just plain Javascript arrays outperformed typed arrays.
joppy··on Is WebAssembly magic performance pixie dust?
As anecdata, I've also found that using typed arrays does not speed up (actually slows down, by 10% or so) code which uses integer arrays. Making sure that arrays stay in packed-small-integer format (which is not always obvious: for example one has to replace x => -x with x => 0-x to avoid the old IEEE -0 from kicking in) consistently outperforms typed arrays for me, by a large margin if allocating many small arrays, and by a small margin if allocating one large array.

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.

joppy··on Go+: Go designed for data science
The first things I would look for in a data science language are multidimensional arrays, linear algebra packages, data frame and time series libraries ... none of which feature on this page.
joppy··on Elon Musk’s anti-union tweet from 2018 must be deleted: U.S. labor board
So in a "free" market, one side has all the bargaining power and the other side has none of it? What does a free market mean to you?
joppy··on Elite philanthropy mainly self-serving
I would have thought that the Gates foundation was an excellent example of how to spent money well: after an enormous amount of research on determining what problems are priorities, and the most efficient (in terms of bang per buck) ways to fix them. I think it is probably best to not look at it through a capitalist or libertarian lens, and actually look at where the money is going, what it is doing, and how effectively it is doing it.
joppy··on A few notes on message passing
Would this help? My understanding is that the copy was so that the new object exists in the heap of the receiving process, and therefore the GC can have a strong guarantee that all objects within a process belong to the GC for that process.

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

joppy··on Euler's Fizzbuzz (2020)
It's roughly the right word for it - mathematicians would say that operations f and g commute if f(g(x)) = g(f(x)) for all x. In this specific case it is not quite true that (^4) and (%15) commute, but if we treat (%15) as returning an element of the-integers-modulo-15 then it is true that the two operations precisely commute. (I think people would say "they commute" anyway, because colloquially it's close enough).
joppy··on Array Databases: Concepts, Standards, Implementations [pdf]
Xarray is absolutely sublime by the way, thank you for your work there. I stuffed around with multi-indices in pandas for a good while before finding Xarray and instantly having all my problems solved :)
joppy··on Intel to Build Silicon for Fully Homomorphic Encryption
Isn’t this the whole problem in homomorphic encryption? We already have cryptographic systems for which encryption is a homomorphism, such as RSA. I thought that the whole problem was finding ways to do it that don’t leak any data, basically.
joppy··on 10 Years of Open-Source Visualization: Did I learn anything from D3.js?
I’ve written some animations in React and Svelte recently which edit SVG properties to move objects, and I found myself making the same rounding optimisation (along with using a single path to draw many circles, rather than many circle objects). I think it’s pretty wild that the only way to position objects in the DOM is with strings.
joppy··on Always Bet on Text (2014)
> “Pictures may be worth a thousand words, when there's a picture to match what you're trying to say.”

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

joppy··on Python Internals Serie: Int (Long) Object
One of my favourite things about Python is that its integers are true integers: they do not overflow or wrap. In my opinion, this is the correct default for most programming languages (in fact, I would have gone a step further and made integer division return fractions rather than floats by default, as well). It removes so many problems (especially for beginners) since it is the way that integers work in the real world: for example x + 1 > x always, with no exceptions.

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.

[1]: https://github.com/golang/go/issues/19623

joppy··on Differentiation Under Integral Sign (2015) [pdf]
I think both! There are parts of mathematics that just look like truths that have been waiting the whole time to be discovered. On the other hand, people invent problem solving techniques which definitely feel more like inventions than discoveries. Then in the middle, there are made-up mathematical structures introduced to bridge between two “clearly discovered” canonical objects, but this made-up structure certainly has the invented flavour.

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.

joppy··on Haskell: The Bad Parts, part 2 (2020)
I don’t know if there is an “issue” with fromIterator, the comment is just that it is ad-hoc: there is no law for it to satisfy.

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).

← PreviousPage 6 of 15Next →