HNHacker News
TopNewBestAskShowJobs

anderskaseorg

2,466 karma · joined September 6, 2014

[ my public key: https://keybase.io/andersk; my proof: https://keybase.io/andersk/sigs/FPXFJpr0VzvWWvxLX4yvXvk0b5m4MuRrVychRgg14gM ]
submissionscomments
anderskaseorg··on On Leibniz Notation
Named arguments are great, but mathematical notation doesn’t have them. x(t) is not a named argument to f(x(t), t), so notation like ∂f(x(t), t)/∂x(t) is nonsense.

If we did have real named arguments like f(a=x(t), b=t), where a and b are fixed by the definition of f rather than arbitrary names to be made up on each invocation, then maybe it would make sense to write something like (∂f/∂a)(a=x(t), b=t). Though that’s still pretty far from Leibniz notation, where ∂f/∂a is somehow supposed to be a numerical value depending on a, not a function to which arguments must be supplied.

But what I think is really lacking in mathematical notation is explicit lambda abstraction: we should be able to write

(λt. f(x(t), t))'(t) = (λa. f(a, t))'(x(t)) x'(t) + (λb. f(x(t), b))'(t),

which reuses the ordinary one-variable derivative and has none of these ambiguities.

anderskaseorg··on Passing nothing is surprisingly difficult
When you allocate memory for an empty array with malloc(num * size) where num == 0, you get a zero-sized memory block. As discussed in the article, representing this with a null pointer causes problems, because that results in undefined behavior in memcpy (despite asking it to copy 0 bytes). So we want it to be a real memory block that can be safely passed to free().
anderskaseorg··on Passing nothing is surprisingly difficult
It is extremely common to have a collection that might or might not be empty at runtime, and we don’t want to force every programmer who allocates a slice to manually write an alternate code path for the empty case.
anderskaseorg··on PHOLED Will Transform Displays
You’re thinking of the Bayer pattern for sensors, which have four equal RGGB squares. When an RGGB pattern is used for displays, the more numerous green subpixels need to be smaller to maintain the correct white balance, resulting in the same 1+1+4 power profile.
anderskaseorg··on Texture Healing for Monospace Fonts
Input Mono is actually monospace. Input Sans and Input Serif are the ones that aren’t (merely monospace-inspired).
anderskaseorg··on Tell HN: Please don't print –-help to stderr in your CLI tools
Specific guidance from GNU:

https://www.gnu.org/prep/standards/html_node/_002d_002dhelp....

> The standard --help option should output brief documentation for how to invoke the program, on standard output, then exit successfully.

anderskaseorg··on Fixing for loops in Go 1.22
Before let/const, scopes could only be introduced at function level, so @babel/plugin-transform-block-scoping transpiles it using an extra function:

    var _loop = function (i) {
      a.push(() => i);
    };
    for (var i = 0; i < 3; i++) {
      _loop(i);
    }
The key is that the scoping happens for each iteration, not around the entire loop. That detail is nonobvious, given how many other languages have gotten it wrong, but I wouldn’t say it’s wild.

(If you’re curious how Babel deals with the more complicated cases of break/continue, labelled break/continue, and return, try it out at https://babeljs.io/repl.)

anderskaseorg··on Fixing for loops in Go 1.22
MDN goes into great detail, but the important point is that JavaScript fixed this with let and const.

    > a = []; for (i = 0; i < 3; i++) a.push(() => i); a.map(f => f())
    [ 3, 3, 3 ]
    > a = []; for (let i = 0; i < 3; i++) a.push(() => i); a.map(f => f())
    [ 0, 1, 2 ]
    > a = []; for (i of "abc") a.push(() => i); a.map(f => f())
    [ 'c', 'c', 'c' ]
    > a = []; for (const i of "abc") a.push(() => i); a.map(f => f())
    [ 'a', 'b', 'c' ]
anderskaseorg··on The SHA256 for this sentence begins with: one, eight, two, a, seven, c and nine.
No. The pigeonhole principle and the birthday attack both apply to situations where you’re looking for two inputs with the same hash as each other, not where you’re looking for one input that describes its own hash.
anderskaseorg··on The SHA256 for this sentence begins with: one, eight, two, a, seven, c and nine.
The pigeonhole principle does not say that. It can be used to show that there are two different sentences with the same hash as each other (among any collection of 2^256 + 1 sentences), but it tells you nothing about hashes that agree with the content of the sentence. The probability that a random hash function on a collection of 2^256 sentences has a fixed point is about 1 - 1/e, and it approaches 1 as you add more variations to grow the collection infinitely. But SHA-256 isn’t actually random, so the only way to know this for sure would be to find an example.
anderskaseorg··on Ad-hoc polymorphism erodes type-safety
It’s useful for Option to be iterable so that it works with things like .flat_map(), and it’s useful to be able to count from an arbitrary iterable.

.iter().count() is a contrived way to find the length of a Vec though, since .len() is clearer and faster.

anderskaseorg··on Montana loses fight against youth climate activists in landmark ruling
Well, starting a lawsuit on behalf of children is the mechanism we have to prevent oppression of children. It’s not about giving them a soapbox and certainly not about giving them the final say. The legal system will not decide that five year olds deserve unlimited Game Boy access just because they claim to want that. It will (one would hope) decide that they deserve a future with a habitable planet because that’s been shown to be in their best interest after the arguments on all sides are weighed, whether the child understands it or not.

(As for edits, maybe you’re responding to someone other than me, but my comment was not edited in any way.)

anderskaseorg··on Montana loses fight against youth climate activists in landmark ruling
A five year old child is a person who has rights. If those rights are being trampled on, then it’s 100% appropriate for the child’s guardians to seek legal recourse on their behalf (provided that it can be shown to the appropriate legal standard that the guardians are acting in the child’s interest, which seems like it wouldn’t be difficult in this case). What the child would say with a soapbox isn’t the relevant question.
anderskaseorg··on Mozilla finds CPU bug (bad store forwarding) in Samsung Galaxy S20
Telemetry is not remote access, much less root access, which does not even exist on typical phones. Phone apps are sandboxed by the OS and can only collect information that the OS allows them to collect. It may of course be possible to abuse this limited information for tracking (and that abuse should absolutely be called out when it occurs), but it’s not the same thing.
anderskaseorg··on Local async executors and why they should be the default
Within your sync callback spawned via spawn_blocking, you can run async code with Handle::current().block_on(…): https://docs.rs/tokio/latest/tokio/runtime/struct.Handle.htm....
anderskaseorg··on Nix/NixOS S3 Update and Recap of Community Call
They are: https://blog.replit.com/betting-on-nix
anderskaseorg··on Nix/NixOS S3 Update and Recap of Community Call
NixOS binary caches are secured by public-key signatures, as well as HTTPS. Hashes for all source artifacts are checked by builders, and this is strongly enforced by a build sandbox that disables network access for derivations without a known output hash. NixOS is leading the effort toward reproducible builds (https://reproducible.nixos.org/), with many sources of nonreproducibility automatically ruled out by design.

Supply chain security is of course a massively multifaceted technical and social problem, but I’m curious what other distributions you think are doing it better in practice?

anderskaseorg··on Ruff: A Fast Python Linter
Ruff actually focuses on checks that don’t overlap with Black. (“By default, Ruff enables Flake8's E and F rules. Ruff supports all rules from the F category, and a subset of the E category, omitting those stylistic rules made obsolete by the use of an autoformatter, like Black.”)

Its import sorting is designed to be compatible with isort --profile=black by default (https://beta.ruff.rs/docs/faq/#how-does-ruffs-import-sorting...). Many isort settings are supported (https://beta.ruff.rs/docs/settings/#isort). I’m not sure what “fly to top” means.

anderskaseorg··on random(random(random(random())))
Of course we care about mathematical rigor.

> Yes, if you define the rng circularly, then you can make the assumption,

We’re agreed then.

> but then you cannot make the claim of 1/2 per term, since they are not uniform.

As mathematicians, we’re allowed to make multi-step arguments. The first step rand(rand()) = rand() * rand() holds for any distribution that scales multiplicatively with the argument, as we’ve agreed. The second step E[rand() * rand()] = E[rand()] * E[rand()] holds for any two independent distributions with finite expectation. The third step E[rand()] = 1/2 holds for this particular distribution. An ideal mathematical rand() satisfies all three of these properties, so this argument that E[rand(rand())] = 1/4 is valid in the mathematical reals. (And it’s approximately valid in the IEEE reals, which is all one typically asks of the IEEE reals.)

anderskaseorg··on random(random(random(random())))
I’m fully aware of how IEEE 754 works, and that there is—to quote my own comment—“a much more complicated generator that takes more care to ensure ideal rounding at the limits of floating-point precision”. If you’re looking for somebody clueless to argue at, you’ll need to go find someone else.

But these details aren’t relevant to this conversation:

• We’re not simulating the weather, we’re drawing dots in a circle—and anyone who cares about the dots being shifted from true uniformity by tiny fractions of the radius of an electron is going to have objections to any conceivable computerized representation of the numbers involved.

• It’s quite reasonable to implicitly treat this discussion as a question within the domain of true mathematical reals, and quite unreasonable to chastise someone as incorrect and careless for doing so.

• It’s still false that “rand(rand()) == rand() * rand() relies on uniform distributions”. This identity between distributions holds with the naïve multiplicative implementation of rand() and any underlying random generator, even a blatantly skewed one, in either the mathematical or IEEE reals, exactly. (And with a more precise IEEE generator, it will hold in as close of an approximate sense as one can expect any mathematical identity to hold when translated to IEEE, which from my perspective is just fine in this context. My only real interest in even acknowledging IEEE here is to support the point that commenters above were correct to consider the identity intuitively obvious, even when you try to throw in that well-actually.)

anderskaseorg··on random(random(random(random())))
It is true, both in the ideal mathematical sense, and also in the IEEE sense under the totally obvious implementation that most programmers would write without any special care:

    function rand(endpoint = 1) {
        return endpoint * Math.random();
    }
Here rand(rand()) would evaluate to literally the same number as rand() * rand() given the same state of the underlying generator. Someone who follows this chain of reasoning is perfectly correct to consider it intuitively obvious.

And rand() * rand() is a great form for analysis, since it allows us to take advantage of E[XY] = E[X]E[Y] for independent X, Y to compute the mean very quickly.

Clearly you followed a different chain of reasoning, and that’s fine; maybe you have a much more complicated generator in mind that takes more care to ensure ideal rounding at the limits of floating-point precision, and that’s fine too. But let’s not admonish anyone for making correct statements.

anderskaseorg··on random(random(random(random())))
Yeah, the problem with your sampling experiment (as your suspicious observation makes it sound like you’ve started to realize) is that this distribution has a very narrow tail to 1 that you won’t hit by accident but nonetheless contributes very significantly to the computation of the mean.

You correctly computed that the mean of log(foo(n)) is exactly −n, and the median of foo(n) indeed shrinks like e^−n (more specifically like e^(1/3 − n), I think), but the mean of foo(n) is exactly 2^−n.

anderskaseorg··on random(random(random(random())))
The claim here is not about expectations. random(X) is identically distributed to random() * X, even if X itself is a random variable, because the randomness in random() is independent from the randomness in X. Indeed, the way you would implement random(X), if you only had random() and X available, would be to compute random() * X.

So in particular, random(random()) is identically distributed to random() * random(). (To be clear, this is different from random()^2, as pointed out elsewhere.)

anderskaseorg··on My thoughts on OCaml
In C and C++, abs(INT_MIN) is undefined behavior. (C17 §7.22.6.1: “The abs, labs, and llabs functions compute the absolute value of an integer j. If the result cannot be represented, the behavior is undefined.” Footnote: “The absolute value of the most negative number cannot be represented in two’s complement.”)
anderskaseorg··on Punctuation Matters: How to use the en dash, em dash and hyphen
It’s the server (probably WordPress’s fault), not JS. &#8211; is an en dash:

    $ curl -s https://www.punctuationmatters.com/en-dash-em-dash-hyphen/ | grep -A6 '<h3>What'
    <h3>What do they look like?</h3>
    <table style="height: 139px;" width="289">
    <tbody>
    <tr>
    <td><strong> &#8211;</strong></td>
    <td><strong> hyphen </strong></td>
    </tr>
anderskaseorg··on Writing the Most Boring Quine
That’s also the idea of https://codegolf.stackexchange.com/questions/48931/make-the-..., where people have taken it up to much larger ordinals.
anderskaseorg··on A 'CSS reset' for TypeScript, improving types for common JavaScript API's
You can safely cast an `unknown` to a desired type with a runtime check from a library like Zod (https://zod.dev/). The `unknown` makes sure you don’t forget a check.
anderskaseorg··on Git archive checksums may change
Nixpkgs’s fetchFromGitHub function hashes the contents of GitHub archives after unpacking, so it’s unaffected.
anderskaseorg··on Ask HN: Why does every package+module system become a Rube Goldberg machine?
Right. Nix bootstraps from a fixed collection of seed binaries, and everything in any given commit of nixpkgs is derived from the seed binaries and versions of software that exist in that commit of nixpkgs. It doesn’t solve the “trusting trust” problem with those seed binaries, but it does solve the “arbitrarily long chains” problem.

(The work at https://www.gnu.org/software/mes/ aims to minimize the needed binary seed to a human-auditable size, and should be applicable to Nix and Guix.)

anderskaseorg··on Ask HN: Why does every package+module system become a Rube Goldberg machine?
Even in the best case where Debian satisfies all the reproducibility goals it sets for itself (i.e. the ~96% at https://tests.reproducible-builds.org/debian/reproducible.ht... becomes 100%), each Debian package is still only a reproducible function of the current versions of its build dependencies at the time it was built. Upgrading those dependencies can change the build output, and this is still normal and expected in Debian; it’s the entire point of a binNMU.

In order to reproduce a given Debian package from scratch, one would need to track down long chains of historical versions of packages from sid. Due to circular dependencies, these chains probably snake back to the very first versions of Debian before the availability of snapshot.debian.org. It’s possible that human judgment could be used to break some chains at an earlier point by substituting different versions, but this goes outside the guarantees provided by Debian’s reproducibility.

Nix guarantees that every package is built with the specified versions of its dependencies, and so on recursively. Some more rebuilding is necessary to maintain this guarantee, but in exchange, it enables stronger notions of reproducibility that Debian can’t.

← PreviousPage 2 of 12Next →