rraval's comments

rraval | 3 years ago | on: Memory Safe Languages in Android 13

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

rraval | 3 years ago | on: Unifying fold left and fold right in Prolog

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.

rraval | 4 years ago | on: Ask HN: What is your Git commit/push flow?

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

rraval | 4 years ago | on: Ask HN: Best alternatives to Google Analytics in 2021?

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

rraval | 4 years ago | on: Nix solves the package manager ejection problem

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

rraval | 4 years ago | on: Nix solves the package manager ejection problem

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

rraval | 4 years ago | on: Show HN: Toph, privacy preserving analytics for my blog

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

rraval | 5 years ago | on: The Great CoffeeScript to Typescript Migration of 2017

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.

rraval | 5 years ago | on: Show HN: Verify JSON using minimal schema

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

rraval | 6 years ago | on: Using Makefile(s) for Go

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

rraval | 6 years ago | on: TypeScript 3.7

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

rraval | 6 years ago | on: Animated Knots: Learn how to tie knots with step-by-step animation

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

rraval | 6 years ago | on: Animated Knots: Learn how to tie knots with step-by-step animation

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

page 1