HN user

rraval

421 karma

Blog: https://zeroindexed.com

[ my public key: https://keybase.io/rraval; my proof: https://keybase.io/rraval/sigs/Ax1_hUoT2TVxiPextem1Y8Vwi61TqJ2pIbE4E19eXqE ]

Posts10
Comments73
View on HN

But ctypes being a terrifying pain in the ass, people tread very carefully around it.

I'm not sure how much people treading carefully actually translates into safety in practice.

CPython in particular has ad-hoc refcounting semantics where references can either be borrowed or stolen and you have to carefully verify both the documentation and implementation of functions you call because it's the wild west and nothing can be trusted: https://docs.python.org/3.9/c-api/intro.html#reference-count...

This ad-hoc borrowed vs stolen references convention bleeds into cffi as well. If you annotate an FFI function as returning `py_object`, cffi assumes that the reference is stolen and thus won't increment the ref count. However, if that same function instead returns a `struct` containing a `py_object`, cffi assumes the reference is borrowed and will increment the ref count instead.

So a harmless looking refactoring that changes a directly returned `py_object` into a composite `struct` containing a `py_object` is now a memory leak.

Memory leaks aren't so bad (even Rust treats them as safe after the leakpocalypse [1] [2]). It's when you go the other way and treat what should have been a borrowed reference as stolen that real bad things happen.

Here's a quick demo that deallocates the `None` singleton:

    Python 3.9.13 (main, May 17 2022, 14:19:07)
    [GCC 11.3.0] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import sys
    >>> sys.getrefcount(None)
    4584
    >>> import ctypes
    >>> ctypes.pythonapi.Py_DecRef.argtypes = [ctypes.py_object]
    >>> for i in range(5000):
    ...     ctypes.pythonapi.Py_DecRef(None)
    ...
    0
    0
    0
    0
    0
    [snip]
    Fatal Python error: none_dealloc: deallocating None
    Python runtime state: initialized

    Current thread 0x00007f28b22b7740 (most recent call first):
      File "<stdin>", line 2 in <module>
    fish: Job 1, 'python3' terminated by signal SIGABRT (Abort)
[1]: https://rust-lang.github.io/rfcs/1066-safe-mem-forget.html [2] https://cglab.ca/~abeinges/blah/everyone-poops/

Related: Graham Hutton's classic paper: a tutorial on the universality and expressiveness of fold

[PDF]: https://www.cs.nott.ac.uk/~pszgmh/fold.pdf

Section 5.1 walks the reader through implementing foldl in terms of foldr after laying down the ground work and gradually introducing things one concept at a time.

For me, the eye-opening insight was using foldr to generate intermediate functions as values, which is the sort of thing "functional" programming does without thinking twice, but is mind bending for someone coming from a more traditional procedural language background.

I separate file backup and version control. I keep every git repository I'm working on in Dropbox, and don't ever worry about how often I'm committing and pushing.

Motivated by a slightly different use case (seamless syncing across multiple machines), I built a custom tool that solves this concern within `git`: https://github.com/rraval/git-nomad

You can toss `git nomad sync` into a systemd timer (or cronjob if you prefer) and forget about it.

I too wanted to preserve privacy and avoid a cookie banner for my blog. I ended up rolling a privacy preserving proxy via Cloudflare workers that forwards `pageview` events to Google Analytics. It's a single HTML tag to drop in and preserves the navigation and user flow reports on the GA side.

See https://github.com/rraval/zeroindexed/tree/master/packages/t...

The blog explains expanded motivation: https://zeroindexed.com/privacy

How do NixOS users typically manage software that is not a Nix package

By writing a Nix package for it (I don't mean for this to sound flippant, tone is a bit hard to convey over text).

For example I have this alpha quality rust binary that I'm developing but I also want a stable version installed at the OS level. I write a Nix package and simply compose it into my overall NixOS configuration alongside the more official Nixpkgs: https://github.com/rraval/nix/blob/master/git-nomad.nix

like a source code tarball where you would traditionally run configure && make && make install?

Nix has a bunch of defaults that make a conventional package like this straightforward.

Here's a package for a vanilla C binary + library that does the `autoreconf && ./configure && make && make install` dance: https://github.com/NixOS/nixpkgs/blob/master/pkgs/tools/secu...

It's almost a little misleading because the actual steps are largely inherited from the defaults, you can read more about `stdenv.mkDerivation` here: https://nixos.org/guides/nix-pills/fundamentals-of-stdenv.ht...

I don't think it's a perfect analogy, but I see what the author is getting at - you need to break the abstraction of some packaging tool, but you want the functionality it provided to still work as well as possible.

You got it. I don't think it's the best analogy either but it is pithy and works if you squint.

The essence is:

1. create-react-app is a monolithic transformation of a bunch of disparate tools into a managed workflow.

2. You can update create-react-app like any other dependency and get updates to the workflow.

3. At any point, you can break the abstraction via `npm run eject`, which drops you down a level into the raw configuration for the "disparate tools" that create-react-app was acting as a veneer over. This ejection is a point in time transformation, you no longer get the managed workflow updates from (2).

The analogy was:

1. The Linux kernel package on $DISTRO is a monolithic transformation of Linux kernel source code to Linux kernel binaries.

2. You can get updates from the package manager.

3. At any point, you can break the abstraction by dropping down a level and forking the current packaging script to adjust the transformation (like applying a patch). This fork is a point in time transformation, you no longer get the package updates from (2).

My blog [1] has expanded commentary on the motivation:

I thought that surely in 2021, somebody would have built a simple page view counter that I could just plug into my dinky blog for minimal cost. I ended up evaluating all the popular alternatives: Matamo, Plausible, and Cloudflare analytics.

As it turns out, all of these are rather expensive. What should have been simple became bespoke and Toph was born. It’s a Cloudflare workers service that sits in between the visitors browser and Google Analytics.

I figured linking to the source code and README was more appropriate for Show HN.

[1] https://zeroindexed.com/analytics

Accumulating loop results in an array is bad for perf, but won't necessarily cause logic bugs.

My favourite implicit return gotcha comes from the intersection of CoffeeScript and jQuery, where implicitly returning `false` from an event handler implies `preventDefault()` and `stopPropagation()`. This can even cause heisenbugs if your logging throws booleans around.

What statically typed language lets me define a type as “number between -180 and +180”, or “string which contains only alphanumeric chars”?

Pretty much all of them. Any simple predicate like this can be encoded with witness types.

Here's an example in Java, which is hardly the paragon of static typing (i.e. it's no Haskell/Idris/Agda/Rust/Typescript):

    class AlphaNumericString {
        private final String str;

        // use a fallible factory with a `private` constructor if you're
        // morally opposed to exceptions
        public AlphaNumericString(String str) throws AlphaNumericException {
            if (!str.matches("^[a-zA-Z0-9]*$")) {
                throw new AlphaNumericException();
            }

            this.str = str;
        }

        private static class AlphaNumericException extends Exception {
        }
    }
Now code can freely use `AlphaNumericString` and be guaranteed that it has been validated.

You may object and say that newtype wrapping is cumbersome but:

1. That's an argument about sugar and ergonomics, not about the semantics that the static type system enforces

2. Some languages make it easier to generate forwarding methods to the underlying type (a la https://kotlinlang.org/docs/reference/delegation.html)

3. The `AlphaNumericString` is describing a smaller set of values than `String`. In general, you should be strongly considering the methods you allow and make sure that all paths continue to enforce the semantics you intend.

This... isn't even using the `make` part of Makefiles at all.

If you look at the final example, every [1] rule is marked as `.PHONY`. `make` bundles 2 capabilities: a dependency graph and an out-of-date check to rebuild files. This demonstration uses neither.

The author would be better served with a shell script and a `case` block. The advantages:

- Functions! The `check-environment` rule is really a function call in disguise.

- No 2 phase execution. The author talks about using variables like `APP`, but those are make variables with very different semantics than shell variables (which are also available inside the recipes).

[1] Yes, there's a `check-environment` "rule" that isn't marked, but it likely should be since it isn't building a file target named `check-environment`.

TypeScript 3.7 7 years ago

That being said though, I'd expect that a tuple would satisfy a `any[]` type.

You have it backwards, the error in question is complaining that `any[]` does not satisfy the tuple type.

Minimal repro:

    function f(x: any[]): [number, string] {
        return x;
    }
The error matches yours:
    Type 'any[]' is missing the following properties from type '[number, string]': 0, 1
From poking the playground, `any[]` hasn't been assignable to tuples since at least v3.3.3

My guess is that the compiler got smarter around reasoning about your `SinonSub` generic and is now forcing you to deal with lingering unsoundness.

That's fair, I too use a trucker's hitch for the ridgeline of a tarp.

However, I'll use adjustable grip hitches for the guylines. That lets me freely adjust how my tarp is placed and holds up the tension reasonably (from experience, the friction hitch on paracord could do with tightening every 1.5 days or so). It's also quicker to tie and uses less cordage, so that's a plus.

Combining the two straight up never occurred to me. Will definitely give that a shot :)

Incidentally, my favourite knot of all time is also listed first: the adjustable grip hitch, https://www.animatedknots.com/adjustable-grip-hitch-knot

Take 5 minutes out of your day, find a small piece of rope (or CAT5, headphone wire, USB cable), and actually tie the knot with your hands. It's super easy to pre-visualize the knot forming and you can learn to tie the knot in any orientation after just a couple tries.

Being able to adjust the tension on the line after tying the knot pops up in all manner of practical situations. This was my gateway drug to the wonderful world of knots.

---

EDIT: to encourage people to actually tie the damn thing, here's a picture of my computer mouse cable around the handle of my coffee cup: https://i.imgur.com/ZN4WXoB.jpg (in the "right handed down" orientation, as opposed the the "right handed up" direction from the instructions).

If the username could be an email address (or the email address is a required field on signup) you don’t have to pass back a message saying that the username is already taken. You simply let them go through the flow and do verification of the email address by emailing and requiring clicking on a link, and if they are already signed up send a message saying someone tried to sign up with your email address.

Sure, let's play this out. Assume for simplicity that the username / user identifier is the email address for our example.

---

Someone comes to your site and tries to sign up with `foo@example.com`. The system has never seen `foo@example.com` so:

The system registers `foo@example.com` in a pending state and sends an email link to `foo@example.com` saying "Please click this link to finish the signup flow".

It also responds to the person signing up, saying "We've sent you an email, please click the link in your inbox to finish the signup flow".

---

Someone comes to your site and tries to signup with `foo@example.com`. The system has an existing record of `foo@example.com` and the password does not match.

The system sends an email to `foo@example.com` saying that someone tried to sign up a new account with the wrong password.

It also responds to the person signing up, saying "We've sent you an email, please click the link in your inbox to finish the signup flow".

---

For a system as described, I concede that a third party has no means of checking if an email address has already signed up, the messages they receive are indistinguishable.

However, for this to work, the click-link-in-email step needs to be a synchronous part of your signup flow. I worry what this added friction does to the bounce rate. Although since I haven't measured it, maybe this worry is misplaced. And of course there's the caveat that measuring with statistically insignificant sample sizes in the early days of a product is its own can of worms.

I disagree with the section titled "Username enumeration and the impact on anonymity".

The author is trying to make the point that you shouldn't tell users that their username is invalid in a password reset flow. The author then goes further and recommends obscuring error messages during the login flow as well.

I find this behaviour to be user hostile and not particularly effective at concealing anonymity. Under the same threat model, a suitably motivated third party can simply attempt to sign up with the same username, and you'll definitely have an error message if the user is already signed up (and that's besides the point that just because a username exists doesn't mean the person behind that identifier actually signed up, anyone could've done it).

The author tries to justify their position by calling it a "slight usability tax" and "a small trade-off for an infrequent process", but my very short experience building a consumer oriented SaaS is that:

1. Password requests are frequent, even with ~5k users

2. Lots of people have more than one email address, and frequently forget which one they used for your service.

3. The support burden simply isn't worth the pseudo-anonymity.

This is my attempt to be sympathetic to the GP's point of view, perhaps they have a different example in mind.

One example comes from the new-style / old-style classes distinction and the method resolution order when considering multiple inheritance.

Python 2.3 introduced "new-style" classes which inherit from `object` and use the C3 resolution algorithm [1]. For backwards compatibility reasons, "old-style" classes without an explicit `object` base class still used the old method resolution semantics.

Python 3 does away with "old-style" classes entirely and all classes must use the "new-style" semantics.

There's presumably a ton of code that doesn't specify `object` as an explicit base class, and thus may have subtle broken behaviour under "new-style". Given Python 3's inability / unwillingness to simulate the old behaviour, I'm unaware of any tool that's able to rewrite Python 2 to 3 in a bullet proof fashion [2].

This is not the same kind of breakage that other people mention, like `print` being a function or `reduce` being moved to `functools`. Those are simply backwards incompatible "movements", not outright removals, and thus can be rewritten by automated tools.

[1] https://www.python.org/download/releases/2.3/mro/ [2] https://portingguide.readthedocs.io/en/latest/classes.html#n...

I would really love to ditch more of my nvim setup for vscode, but this bug [1] from 3 months ago absolutely wrecks my productivity. I rely on splits as the primary means to navigate the codebase and having a single cursor position per file is maddening!

The vim bindings aren't perfect and are sometimes slow (you can see a macro insert things character by character) but about on-par with IdeaVim for the IntelliJ side feature wise.

Maybe oni [2] will catch up before vscode fixes their issues but I eagerly await a decent language server modal editing setup.

[1] https://github.com/VSCodeVim/Vim/issues/2688 [2] https://github.com/onivim/oni

My startup's monorepo has 2 root commits as well. When the company was first starting out, my co-founder and I created independent git repos. I was writing OCR type research-y code and he was doing more traditional CRUD REST webserver things.

When it came time to pull things together, we thought it'd be fun to try and maintain the histories. So I added his repo as a remote and simply merged his unrelated history into mine.

Fast forward and now we offer hoodies as swag to anyone who contributes to the repo. We personalize the hoodie with your git username, the truncated commit hash of your first commit, and the number of parent commits to your first commit.

Having 2 root commits means that both my cofounder and I have hoodies with a large 0 as number of parent commits. Just a nice way to commemorate this accident of history :)

Cool, I didn't realize that. Thanks for the correction.

I should mention that this was back in September 2014, where IBM was the only one offering anything in Canada (hence the migration).

Once we'd done all the work to decouple from AWS, it was fairly straightforward when Azure came knocking and offered us a ton of compute credits from their startup program. IBM had also rebooted a couple of critical VMs with no prior warning which left us wanting a cloud provider that took its tenants seriously. This was in ~May 2016 and we hadn't heard anything about a Canadian AWS datacentre, which apparently launched in Dec 2016.

We still use SES to this day because we haven't found a good alternative, so it's not like we hate AWS or anything. Just offering a (dated) perspective on why someone might choose Azure.