HN user

NobodyNada

3,532 karma
Posts12
Comments615
View on HN

I think "storing deltas" based on seed is largely how Minecraft saves the level already

This is not the case, level data is stored in full. One reason for this is that level generation is pretty slow (compare the time it takes to create a new world vs. load an existing one); another reason is that it changes between versions.

Beato doesn't do much hour-long unscripted talking to the camera. Those videos are typically only about 10 minutes long.

The videos are heavily edited with many cuts. So, I'd guess the production process is "have him talk for an hour, then edit it down to a 10 minute YouTube video".

In any event, Rick Beato seems like a normal human being, albeit a somewhat goofy dad-rock oldie. 12Tone comes across as an aspie weirdo. I'd rather have more of the former.

And that's...kind of the point I'm making? The amount of effort put into the content of Beato's videos is quite low, with probably a couple hours at most of time put into research, coming up with talking points, and recording. But a lot of effort is spent on production, in order to make content that feels relatable and punchy and palatable to a mass market. It's junk food, and junk food sells well.

"The Real Reason Music is Getting Worse"

As a musician myself, and as someone who enjoys watching music and music theory content on YouTube, I can't stand watching Rick Beato because of stuff like this. There certainly are valid criticisms to be made against the music industry, but those videos are like 90% ridiculous cherry-picked comparisons and vague, superficial justifications. It's low-quality nostalgia-driven engagement bait that's optimizing for clicks and comments.

I really like this video by 12tone addressing Beato's schtick about modern music [0]. 12tone is an example of someone who I consider produces much higher quality content than Beato, and obviously puts a ton of time and effort into the videos (writing scripts, drawing the visuals, recording and editing, and making moderately-clickbaity titles and thumbnails). But I'd guess it's probably a one-person operation.

In comparison, Beato's channel strikes me as low-effort content (pick a topic and spend an hour talking unscripted in front of a camera) with a ton of money spent on production, editing, and algorithm hyper-optimization to maximize ROI. It's such a straightforward case of something that would not exist without YouTube's monetization system that it's rather ironic for the parent to use it as an example otherwise.

[0] https://www.youtube.com/watch?v=tODG4Xt45bU

10 years ago, GitHub had a far better reputation and the Rust ecosystem was much smaller and less load-bearing, so "what if someone doesn't have a GitHub account" was a theoretical concern for most people. So the issue was a low-priority backlog item that everyone agreed would be nice-to-have but there weren't enough people willing to volunteer their time to it over more important and more impactful work.

Obviously, the situation has changed in recent years, so it's now considered a much higher priority by many people and some of them are actively working on it. But it's a lot of work to be done by volunteers, so it takes time.

That's the reality of open-source projects: things get done when they are important enough to motivate someone to either fund it or work on in their free time, not according to idyllic roadmaps and schedules.

Using "if" or "unless", whichever is more appropriate, is far more readable than "guard".

How is a different (and longer) keyword "far more readable"? That's just a matter of preference and familiarity. The reason for choosing a different keyword is that it's not quite equivalent to an unless as the {} block must exit the surrounding scope. You read it like an assert statement with a custom handler.

Moreover, there are many languages where an assignment or an initialization can appear in any place where an expression can appear. Such general rules are always better than special rules like allowing new bindings in a "guard", but not in an "if".

You can introduce bindings in an if too. The special thing about guard is that you can introduce a binding which is valid for the remainder of the scope outside the {} block (where the condition is true) but not inside (where the condition is false).

guard is an inverted if statement, with the additional requirement that the branch must exit the parent scope. It's useful sometimes for readability, particularly for avoiding the "pyramid of doom" when you have a lot of preconditions that need to be checked:

    if fooOK 
        if barOK {
            if bazOK {
                // do something
            }
        }
    }
can be written as:
    guard fooOK else { return }
    guard barOK else { return }
    guard bazOK else { return }
    // do something
Obviously there are other options (like writing a negated if), but sometimes guard is more readable. It's a style thing.

The more important use case for guard is that 'guard let' statements can pattern-match and introduce bindings that are valid for the rest of the scope:

    guard let foo = someOptional else { return }
    print(foo);
This is useful enough that Rust copied it in the form of 'let ... else {}' statements (but did not bring over boolean guard statements).

Most people without absolute pitch have some level of "pitch memory", but it's not comprehensive or reliable. For instance, if you ask people to sing a pop song, they're significantly more likely than chance to sing it in the original key.

I know the Super Mario Bros. theme starts on an E, so I can identify an unknown pitch by recalling that theme and comparing using relative pitch. But that's quite a slow and unintuitive process, and it's easy to make a mistake. People with absolute pitch just hear the pitch without having to "recall" a reference note to compare to like that.

I'm a musician who doesn't have absolute pitch, but does have very strong relative pitch. My understanding is that perfect pitch is neat party trick, but actually a hindrance instead of a help in most musical circumstances. Relative pitch, on the other hand, is incredibly useful (and fortunately you can train and develop it later in life).

Because most people don't have perfect pitch, (Western) music is built on the relationships between pitches rather than the absolute pitches. So with absolute pitch, you can play something by ear; with relative pitch, you can play something by ear in any key.

Learning to think of the notes you're playing relatively instead of absolutely is already a difficult leap for most musicians, and my understanding (though I don't have absolute pitch so I can't compare from experience) is that absolute pitch makes this skill significantly harder to acquire, since you have to retrain your ear in addition to your hands.

If I were offered a choice to trade my sense of relative pitch for absolute pitch, I most certainly would not take it. I know well the feeling of incongruity when my muscle memory is stuck in the wrong key, and absolute pitch would mean I'm stuck there all the time instead of being just able to shake my head, focus on the new key, and clear my mind of the old.

I hate compilers 1 month ago

The engineer who wants the build to be reproducible and the engineer who wants to have the build time in the compiled binary may not be the same person.

The reason for this is to ensure stack overflows are detected. The OS places a guard page above the top of the stack, which will cause a segfault if accessed. That way stack overflows are guaranteed to crash rather than stomping on valid memory that belongs to something else. However, if a stack frame is larger than a page (say, because it includes a large buffer), then it is possible for the program to "jump over" the guard page and access memory beyond.

In order to protect against this, the compiler inserts some dummy reads or writes as needed to ensure every page is touched in order from bottom to top. This ensures the guard page is hit before the application has a chance to write to memory beyond it.

Here's an example: https://godbolt.org/z/oTbzTczM6

The headphones are from AIAIAI: https://aiaiai.audio/headphones/tma-2-studio-wireless

Loopback looks nice, but I prefer to keep the routing entirely within my DAW (or my interface where possible) to keep latency to a minimum, particularly since I'm already using wireless headphones that add 10ms latency.

Another mitigation I now have is to use an aggregate device that has BlackHole on the first two channels rather than the main outputs. That way, if I accidentally start playing audio, it gets harmlessly sent to the void; and it also means I can easily capture and forward it in my DAW if I actually want to play PC audio (for example, listening to a recording during a rehearsal).

I needed something like this a few months ago. I use my MacBook to run my (musical) keyboard rig for live performances, and use low-latency wireless headphones for monitoring. The headphones have a transmitter dongle that plugs into my laptop, and the dongle sends a "play/pause" command if I press a button on my headphones...causing Music to launch and begin playing audio out of my default output device. It doesn't even care whether my headphone transmitter is selected as the default output device; in a complex multi-device setup, I can press a button on my headphones and it will happily play audio out of some other device.

This is problematic because if I were to accidentally hit the button in the middle of a set, and it decides to default to whatever interface is connected to the P.A. system, then now I've just started blasting some random song at full volume to everyone in the venue.

(It's not an immediate problem for me anymore because I've reworked my hardware setup such that the dongle connects through my audio interface rather than directly to my laptop, meaning my laptop no longer receives "play/pause" commands from it. There were additional reasons for this rework, but preventing this misbehavior was absolutely part of the consideration.)

It's absurd that a premium device marketed to creative professionals has unconfigurable behavior like this which is so unacceptable for a live show.

(Not the parent, but I'm faceblind as well)

The interaction described goes like this:

"Hi there, I'm ABC, nice to meet you, what's your name"

"...Huh? I'm XYZ. We've met before."

"Oh right...sorry, I promise I remember you! We knew each other from there, and we've worked on this and that together, and etc. etc. etc. I'm just terrible with faces, I'm so sorry!"

It's not "you know things about them without recognizing them"; it's "you don't recognize them at first, it gets awkward, and so you recite facts about them prove that you didn't forget who they were"

I recently learned that I have some level of face-blindness (I took the CFMT online and scored 43).

It's something I've had my whole life but only recently realized wasn't "normal". It's not like I can't recognize people at all, but rather that faces aren't very distinctive to me compared to other identifying characteristics (such as hair color/style/length, clothing, skin tone, height, voice, gait, mannerisms, etc.) It takes me a while to learn to distinguish everyone in a group of people (especially people who are similar along all of those attributes), but once I know someone well I will usually recognize them without problems.

The only real issues are when someone changes their appearance (e.g. getting glasses or shaving a beard), or when I run into someone in an unexpected context (like randomly meeting someone I know on the street). A few months ago I ran into my cousin at an event in another city, and didn't recognize her until after 20 or 30 seconds of conversation.

It's also not usually too hard to mask. I realized I have a subconscious habit of never greeting people by name because I'm always afraid of getting it wrong, and it's easy enough to bluff through "oh hi, how are you, good to see you, what have you been up to" pleasantries until I figure out who I'm talking to. The most awkward situations are when I'm unsure whether or not I know someone and have to risk either mistaking a stranger for a friend, or accidentally ignoring/reintroducing myself to an acquaintance. Also, starting a new TV show sucks.

Now that I know it's an actual condition with a name, I'm not sure yet whether it makes things better or worse if I try to explain it to people to excuse my mistakes.

If any other face blind people have useful tips or experiences, I'm all ears :)

My only gripe is that a lot of it is feeling a bit kick-starter-y

IMO the term "project goals" is quite misleading for what this actually is. A project goal is a system for one person (or a small group of people) to express that they'd like to work on something and ask for Rust project volunteers to commit ongoing time and effort to supporting them through code review, answering questions, etc. It doesn't mean that the Rust project itself has set the goal, or even necessarily endorsed it.

So it's not quite right to treat it as a formal roadmap for Rust, just a "there are some contributors interested in working on these areas".

if optional were a general sum type you wouldn't be able to make these optimizations easily without extra information

Rust has these optimizations (called "niche optimizations") for all sum types. If a type has any unused or invalid bit patterns, then those can be used for enum discriminants, e.g.:

- References cannot be null, so the zero value is a niche

- References must be aligned properly for the target type, so a reference to a type with alignment 4 has a niche in the bottom 2 bits

- bool only uses two values of the 256 in a byte, so the other 254 form a niche

There's limitations though, in that you still must be able to create and pass around pointers to values contained within enum, and so the representation of a type cannot change just because it's placed within an enum. So, for example, the following enum is one byte in size:

    enum Foo {
        A(bool),
        B
    }
Variant A uses the valid bool values 0 and 1, whereas variant B uses some other bit pattern (maybe 2).

But this enum must be two bytes in size:

    enum Foo {
        A(bool),
        B(bool)
    }
 
...because bool always has bit patterns 0 and 1, so it's not possible for an invalid value for A's fields to hold a valid value for B's fields.

You also can't stuff niches in padding bytes between struct fields, because code that operates on the struct is allowed to clobber the padding.

There are lots of unstable things in Rust that have been unstable for many years, but this isn't one of them. openat() was added in September, and the next PR in the series implementing unlinkat() and removeat() received a code review three weeks ago and is currently waiting on the author for minor revisions.

As long as it's unstable it's totally fair to say Rust's stdlib does not expose them. You might as well say it's fixed because someone posted a patch on a mailing list somewhere

Agreed. My comment was intended to be read as "it's planned and being worked on", not "it's available".

Nearly every available filesystem API in Rust's stdlib maps one-to-one with a Unix syscall (see Rust's std::fs module [0] for reference -- for example, the `File` struct is just a wrapper around a file descriptor, and its associated methods are essentially just the syscalls you can perform on file descriptors). The only exceptions are a few helper functions like `read_to_string` or `create_dir_all` that perform slightly higher-level operations.

And, yeah, the Unix syscalls are very prone to mistakes like this. For example, Unix's `rename` syscall takes two paths as arguments; you can't rename a file by handle; and so Rust has a `rename` function that takes two paths rather than an associated function on a `File`. Rust exposes path-based APIs where Unix exposes path-based APIs, and file-handle-based APIs where Unix exposes file-handle-based APIs.

So I agree that Rust's stdilb is somewhat mistake prone; not so much because it's being opinionated and "nudg[ing] the developer towards using neat APIs", but because it's so low-level that it's not offering much "safety" in filesystem access over raw syscalls beyond ensuring that you didn't write a buffer overflow.

[0] https://doc.rust-lang.org/std/fs/index.html

Waymo in Portland 3 months ago

Payroll taxes would not pay for things like mass transit

In Oregon, TriMet is funded by a payroll tax: https://www.oregon.gov/dor/programs/businesses/pages/trimet-...

The Oregon Department of Revenue administers tax programs for the Tri-County Metropolitan Trans­portation District (TriMet). Nearly every employer who pays wages for services performed in this district must pay transit payroll tax.

The transit tax is imposed directly on the employer. The tax is figured only on the amount of gross payroll for services performed within the TriMet Transit District. This includes traveling sales repre­sentatives and employees working from home.

you built what was basically a raspberry pi with a microcontroller by hand, and you had to use the dumb speaker and controller to make your own music firmware to produce notes

This sounds like they were most likely bit banging square waves into a speaker directly via a GPIO on a microcontroller (or maybe using a PWM output if they were fancy about it). In that case, the audio frequency will be derived directly from the microcontroller's clock speed, and the tolerance of an internal oscillator on a microcontroller can be as bad as 10%.

I have a setup based on this, but I modified it to encrypt the SSH host key using the TPM. That way, I can detect a MiTM from an attacker who has stolen the drive or modified the boot policy because host key verification will fail.

/usr/lib/dracut/modules.d/46cryptssh:

    #!/bin/bash
    
    check() {
        require_binaries sshd || return 1
        return 0
    }
    
    depends() {
        return 0
    }
    
    install() {
        if [ ! -e /etc/ssh/dracut ]; then
            mkdir /etc/ssh/dracut &&
            tmp=$(mktemp -d) &&
            head -c128 /dev/random > $tmp/passphrase &&
            ssh-keygen -t ed25519 -f /etc/ssh/dracut/ssh_host_ed25519_key -N"$(base64 < $tmp/passphrase)" &&
            tpm2_createprimary -C o -c $tmp/primary.ctx &&
            tpm2_pcrread -o $tmp/pcr.bin sha256:0,7
            tpm2_createpolicy --policy-pcr -l sha256:0,7 -f $tmp/pcr.bin -L $tmp/pcr.policy
            tpm2_create -C $tmp/primary.ctx -L $tmp/pcr.policy -i $tmp/passphrase -c $tmp/seal.ctx &&
            tpm2_evictcontrol -C o -c $tmp/seal.ctx -o /etc/ssh/dracut/seal || {
                rm -r $tmp /etc/ssh/dracut
                exit 1
            }
            rm -r $tmp
        fi
        for file in /etc/ssh/dracut/*; do
            inst_simple "$file" "/etc/ssh/${file#/etc/ssh/dracut/}"
        done
    
        mkdir -p -m 0700 "$initdir"/root/.ssh
        /usr/bin/install -m 600 /etc/ssh/dracut_authorized_keys "$initdir"/root/.ssh/authorized_keys
    
        inst_binary /usr/sbin/sshd
        inst_binary /usr/sbin/ssh-keygen
        inst_binary /usr/bin/tpm2_unseal
        inst_binary /usr/bin/base64
        inst_simple /usr/lib/libtss2-tcti-device.so
        
        inst_simple "$moddir"/cryptsshd.service "$systemdsystemunitdir"/cryptsshd.service
        inst_simple "$moddir"/sshd_config /etc/ssh/sshd_config
    
        inst_binary /usr/lib/ssh/sshd-session
        inst_binary /usr/lib/ssh/sshd-auth
    
        mkdir -p -m 0755 "$initdir"/var/empty/sshd
        mkdir -p -m 0755 "$initdir"/usr/share/empty.sshd
        mkdir -p  -m 0755 "$initdir"/var/log
        touch "$initdir"/var/log/lastlog
    
        systemctl -q --root "$initdir" enable cryptsshd
    }
cryptsshd.service:
    [Unit]
    Description=OpenSSH Daemon for Disk Encryption Passphrase
    DefaultDependencies=no
    Before=cryptsetup.target
    After=network-online.target
    
    [Service]
    Type=notify-reload
    ExecStartPre=/bin/sh -c '/usr/bin/ssh-keygen -p -f /etc/ssh/ssh_host_ed25519_key \
        -N "" -P "$(/usr/bin/tpm2_unseal -c /etc/ssh/seal -p pcr:sha256:0,7 | base64)"'
    ExecStart=/usr/bin/sshd -D
    KillMode=process
    Restart=always
    
    [Install]
    WantedBy=sysinit.target
That encrypts the SSH host key using a password sealed with PCR7, which is invalidated if an attacker disables Secure Boot or tampers with the enrolled keys. Thus, an attacker can't extract the key from the drive or by modifying the kernel command line to boot to a shell (since that's not allowed without disabling secure boot).

It's still probably vulnerable to a cold boot attack, since the key is decrypted CPU-side. It would be interesting to perform the actual key operations on the TPM itself to prevent this.

Rathbun's Operator 5 months ago

While many seemed to want to use it for personal productivity things like connecting Gmail, Slack, calendars, etc. that didn’t seem interesting to me much. I thought why not have it solve the mundane boring thigns that matter in opensource scientific codes and related packages.

This, here, is the root of the issue: "I'm not interested in using an AI agent for my own problems, I want to unleash it on other people's problems."

The author is trying to paint this as somehow providing altruistic contributions to the projects, but you don't even have to ask to know these contributions will be unwelcome. If maintainers wanted AI agent contributions, they would have just deployed the AI agents themselves. Setting up a bot on behalf of someone else without their consent or even knowledge is an outlandishly rude thing to do -- you wouldn't set up a code coverage bot or a linter to run on a stranger's GitHub project; why would anyone ever think this is okay?

This is the same kind of person who, when asked a question, responds with a copypasted ChatGPT reply. If I wanted the GPT answer, I would have just asked it directly! Being an unsolicited middleman between another person and an AI brings absolutely no value to anybody.

There's a difference in effort of several orders of magnitude between "change a setting so the compiler doesn't emit multiplies" and "convince GCC/LLVM to add a special-case flag for one very rare chip, or maintain your own fork". The vendor's workaround is the "ideal" solution, but disabling multiplies is a lot more practical if you don't need the performance.

They also mention in the next sentence that they adopted the "correct" workaround (by providing a multiplication library function for the compiler to call).

The idea is well-intentioned, but implementing it by making drivers try to parse arbitrarily complex conditionals while driving is unwise.

There's a sign near my house for a school zone with a reduced speed limit, that used to have conditions similar to the GP's example (though not quite as bad) But they recently attached a yellow light to the top of the sign and changed the condition to "when flashing." That's a much more effective solution.

For what it's worth, personally I thought your writing style and sense of humor was excellent, and my favorite part of the post.

I also appreciate you giving me an updated copy of the "Microsoft is a corporation" meme, as the one I have downloaded seems to become outdated each time a new Windows update comes out.