HN user

jez

4,068 karma

- http://blog.jez.io

- https://github.com/jez

- https://jez.io

[ my public key: https://keybase.io/jez_; my proof: https://keybase.io/jez_/sigs/s9uEJ_STwo02blZ-iSkntb4jURsdxhKkc8rg5Y_9im8 ]

Posts101
Comments284
View on HN
maxmautner.com 1mo ago

YIMBY data projects, between naps

jez
2pts0
maxmautner.com 2mo ago

Lessons from 6 Years of Local Advocacy

jez
2pts0
maxmautner.com 3mo ago

The Declining Driver's License: Good, Bad, or Both?

jez
7pts0
stripe.dev 3mo ago

Fast CI for a 50M-line Ruby monorepo

jez
4pts0
substack.com 4mo ago

The Summer Slide, part 3: The tax code we had

jez
1pts1
causalityineconomics.com 4mo ago

FOMC Insight Engine: semantic search over Fed archives

jez
2pts0
stripe.com 4mo ago

Stripe valued at $159B, 2025 annual letter

jez
238pts245
basta.substack.com 5mo ago

The imminent risk of vibe coding

jez
1pts0
www.youtube.com 6mo ago

Fixing an 11th hour Street Fighter II GFX ROM bug [video]

jez
2pts0
css4.pub 6mo ago

Making beautiful PDF documents from HTML and CSS

jez
5pts0
abseil.io 7mo ago

Deprecation: Software Engineering at Google

jez
4pts1
www.youtube.com 9mo ago

Calm traffic needs more than calm driving [video]

jez
2pts0
techcrunch.com 9mo ago

Anthropic hires new CTO with focus on AI infrastructure

jez
4pts1
tony-zorman.com 10mo ago

Dissertation Typesetting Considerations

jez
2pts0
www.thebignewsletter.com 11mo ago

How did a debate over housing become a call to end the anti-monopoly movement?

jez
13pts2
basta.substack.com 11mo ago

Doing versus Delegating

jez
3pts0
www.halfwayanywhere.com 1y ago

2024 Pacific Crest Trail Hiker Survey

jez
2pts0
github.com 1y ago

Hotspot: Linux `perf` GUI for performance analysis

jez
114pts17
twitter.com 1y ago

Preview release of ty, a type checker for Python

jez
3pts0
notes.eatonphil.com 1y ago

Burn Your Title

jez
13pts10
www.culawreview.org 1y ago

A Comparative Look at Antitrust Standards in the US and EU

jez
2pts1
danmackinlay.name 1y ago

Dropbox for Dropbox Haters

jez
5pts0
jamesbvaughan.com 1y ago

A surprising scam email that evaded Gmail's spam filter

jez
5pts1
graydon2.dreamwidth.org 1y ago

Origins and Development

jez
2pts0
typedetail.com 1y ago

Type Detail: the beautiful details of typefaces

jez
2pts0
ryanwwest.com 1y ago

Multi-platform, multi-format annotation woes

jez
2pts0
www.youtube.com 1y ago

The Elo Rating System [video]

jez
1pts0
www.youtube.com 1y ago

Checkerboard Math: Binary arithmetic with bit shifts [video]

jez
1pts0
blog.thestateofme.com 1y ago

GL.iNet MT-6000 Flint2 Review

jez
2pts0
rachitsingh.com 1y ago

Collaborating Effectively with Jupyter Notebooks

jez
3pts0

Additionally, some repos can be configured to automatically merge PRs when all requirements are met, one of which might be your approval.

If anyone at GitHub is reading this, I’d love a fourth checkbox in the “leave a review” modal that is “Approve but disable auto merge” (alongside Comment/Approve/Request changes)! Even just surfacing “this PR has auto merge enabled” near the Approve button would be great.

I want the same mode, but on iOS! Imagine carrying nothing but the phone in your pocket, sitting down at your desk, plugging your phone into the monitor, which has your keyboard and mouse docked, and you have a full development environment.

Even in Vim, the editing experience falls over when making markdown tables that have non-trivial content in their cells (multiple paragraphs, a code block, etc.). I recently learned that reStructuredText supports something called "list tables":

https://docutils.sourceforge.io/docs/ref/rst/directives.html...

Where a table is specified as a depth-2 list and then post processed into a table. Lists support the full range of block elements already: you can have multiple paragraphs, code blocks, more lists, etc. inside a list item.

This syntax inspired the author of Markdoc[1] (who came from an rST background) to support tables using `<hr>`-separated lists[2] instead of nested lists (to provide more visual separation between rows).

I have found various implementations of list table filters for Pandoc markdown[3][4], but have never gotten around to using any of them (and I've tossed around ideas of implementing my own).

[1] https://markdoc.dev

[2] https://markdoc.dev/docs/tags#table

[3] https://github.com/pandoc-ext/list-table

[4] https://github.com/bpj/pandoc-list-table

A more complicated version of this problem exists in TypeScript and Ruby, where there are only arrays. Python’s case is considerably simpler by also having tuples, whose length is fixed at the time of assignment.

In Python, `x = []` should always have a `list[…]` type inferred. In TypeScript and Ruby, the inferred type needs to account for the fact that `x` is valid to pass to a function which takes the empty tuple (empty array literal type) as well as a function that takes an array. So the Python strategy #1 in the article of defaulting to `list[Any]` does not work because it rejects passing `[]` to a function declared as taking `[]`.

I have used this in the past when building shell scripts and Makefiles to orchestrate an existing build system:

https://github.com/jez/symbol/blob/master/scaffold/symbol#L1...

The existing build system I did not have control over, and would produce output on stdout/stderr. I wanted my build scripts to be able to only show the output from the build system if building failed (and there might have been multiple build system invocations leading to that failure). I also wanted the second level to be able to log progress messages that were shown to the user immediately on stdout.

    Level 1: create fd=3, capture fd 1/2 (done in one place at the top-level)
    Level 2: log progress messages to fd=3 so the user knows what's happening
    Level 3: original build system, will log to fd 1/2, but will be captured
It was janky and it's not a project I have a need for anymore, but it was technically a real world use case.

Another fun consequence of this is that you can initialize otherwise-unset file descriptors this way:

    $ cat foo.sh
    #!/usr/bin/env bash

    >&1 echo "will print on stdout"
    >&2 echo "will print on stderr"
    >&3 echo "will print on fd 3"

    $ ./foo.sh 3>&1 1>/dev/null 2>/dev/null
    will print on fd 3
It's a trick you can use if you've got a super chatty script or set of scripts, you want to silence or slurp up all of their output, but you still want to allow some mechanism for printing directly to the terminal.

The danger is that if you don't open it before running the script, you'll get an error:

    $ ./foo.sh
    will print on stdout
    will print on stderr
    ./foo.sh: line 5: 3: Bad file descriptor

The tender offer announced in the article is open to former employees as well, so they personally profit regardless of Stripe being public (unless the claim is that by being public the valuation would be materially higher than the stated valuation for this offer).

As others have mentioned, it comes down to the threat model, but sometimes the threat model itself is uncomfortable to talk about.

It’s sad to think about, but in my recollection a lot of intra-building badge readers went up in response to the 2018 active shooter situation at the YouTube HQ[1]. In cases like this, the threat model is “confine a hostile person to a specific part of the building once they’ve gotten in while law enforcement arrives,” less than preventing someone from coat tailing their way into the building at all.

[1] https://news.ycombinator.com/item?id=16748529

Something I never understood about this: is the pipe necessary, or just to have another symbol contributing to the mayhem?

    :(){:&;:};:
This is the same number of characters but doesn’t use a pipe, and I was never able to figure out why it seems so universally to use a pipe.

Far be it from me to get in the way of someone protesting megabank centralization, but...

I have to imagine that this bank relationship will be different from those previous acquisitions? I never interacted with Goldman Sachs for the duration I've had my Apple Card—the relationship is entirely with Apple and their iOS app. I don't imagine that to be much different when Chase is the issuer.

Does that mean that this new partnership will be better because Chase is better at consumer banking than Goldman Sachs, or Chase negotiated a deal that will not cause them to lose a lot of money?

If it's the latter, does that mean that the card rewards for Apple Card will get worse?

Clicks Communicator 7 months ago

If you tap and hold a second thumb after you’ve tapped and held to bring up the moveable cursor, it switches to a selection range.

Less can be configured with a ~/.lesskey file

I have a single line in my config[1] which binds s to back-scroll, so that d and s are right next to each other and I can quickly page up/down with one hand.

If you’re on macOS, you may not be able to use this unless you install less from Homebrew, or otherwise replace the default less.[2]

[1] https://github.com/jez/dotfiles/blob/master/lesskey#L2

[2] https://apple.stackexchange.com/questions/27269/is-less1-mis...

What’s the approach to embedding fonts in standard ebooks’ epubs? Curious whether there’s a set of fonts that producers are allowed to embed in finished books, or whether there’s project consciously avoids embedding fonts, and if so why.

I tried to find a policy page on this via a standardebooks.org site search but nothing looked relevant.

I’m asking after realizing that some of my favorite books were books where the ebook had intentional font choices, for example different fonts for chapter titles vs body text, fonts that matched the vibe of the book (historical, more modern, etc.) It would be nice if more ebook readers made it easy to import more than the ~8 fonts they include by default but the next best thing is when the book itself includes a great font.

For another perspective on "lying to the compiler," I enjoyed the section on Loopholes in Niklaus Wirth's "Good Ideas, Through the Looking Glass"[1]. An excerpt:

Experience showed that normal users will not shy away from using the loophole, but rather enthusiastically grab on to it as a wonderful feature that they use wherever possible. This is particularly so if manuals caution against its use.

[...]

The presence of a loophole facility usually points to a deficiency in the language proper, revealing that certain things could not be expressed.

Wirth's use of loophole most closely aligns with the unchecked casts that the article uses. I don't think exceptions amount to lying to the compiler. They amount more to assuming for sake of contradiction, which is not quite lying (e.g., AFSOC is a valid proof technique, but proofs can be wrong). Null as a form of lying is not the fault of the programmer, that's more the fault of the language, so again doesn't feel like lying.

[1] https://people.inf.ethz.ch/wirth/Articles/GoodIdeas.pdf

I have maintained a pandoc filter in Haskell for a while. Pandoc is written in Haskell, and so takes a similar approach: JSON API for arbitrary languages, but with a library for trivially parsing that JSON back into the same Haskell datatype that Pandoc uses under the hood.

When you sit down to write the filter for the first time, it’s amazing. You’re using a typed IR that’s well documented, a language that catches you when you’re making mistakes, etc. You have to do very little boring grunt work and focus only on what the filter needs to do.

Over time, the filter became feature complete, so I didn’t want to have to touch it anymore, but the library for parsing JSON releases a new version for every new feature in the AST, and the parsing function checks that the version your filter was compiled with is at least as new as the pandoc that produced the JSON. It has to, because if the pandoc is newer, your older filter won’t know how to parse some of the nodes.

My filter is feature complete, and shouldn’t need to look at those nodes or their new fields: needing to upgrade is just toil. But over time, pandoc releases new versions, and I’d need to recompile the filter to build the new version. At those points, I also found myself having to deal with library, build tool, OS, or compiler upgrades, all to recompile a filter that should need to be.

Eventually I switched to Pandoc lua filters, which eliminated the toil while also being platform agnostic (and not requiring any sort of notarization or executable quarantine on enterprise systems) at the expense of having to tolerate Lua the language. Now, new versions of Pandoc don’t require me to boop a version number in my filter. If that wasn’t an option, for any future filters I write, I’d write my own JSON parser that only parses as much of the JSON as I needed, leaving the rest untouched—that way it wouldn’t matter if new changes came along. I could even tolerate backwards incompatible changes as long as they didn’t alter the contract of the narrow focus of that one filter!

There are of course other ways to deal with problems like these (protocol buffers, JSON parsing with an option to throw away unrecognized JSON, etc. etc.)

I have not looked at how mdBook plugins handle this. But if I were writing such a plugin, it’s the first thing I’d look at, and be sure to program around.

It's an interesting approach. From my skim, the way it works:

1. Parse the files with a Ruby parser, collect all method definition nodes

2. Using location information in the parsed AST, and the source text of the that was parsed, splice the parameters into two lambda expressions, like this[1]:

     "-> (#{method_node.parameters.slice}) {}"
3. Evaluate the first lambda. This lets you reflect on `lambda.parameters`, which will tell you the parameter names and whether they're required at runtime, not just statically

4. In the body of the second lambda, use the `lambda.parameters` of the first lambda in combination with `binding.get_local_variable(param_name)`. This allows you to get the runtime value of the statically-parsed default parameters.

This is an interesting and ambitious architecture.

I had though in the past about how you might be able to get such a syntax to work in pure Ruby, but gave up because there is no built-in reflection API to get the parameter default values—the `Method#parameters` and `UnboundMethod#parameters` methods only give you the names of the parameters and whether they are optional or required, not their default values if they are optional.

This approach, being powered by `binding` and string splicing, suffers from problems where a name like `String` might mean `::String` in one context, or `OuterClass::String` in another context. For example:

    class MyClass
      include LowType
      class String; end
      def say_hello(greeting: String); end
    end

    MyClass.new.say_hello(greeting: "hello")
This program does not raise an exception when run, despite not passing a `MyClass::String` instance to `say_hello`. The current implementation evaluates the spliced method parameters in the context of a `binding` inside its internal plumbing, not a binding tied to the definition of the `say_hello` method.

An author could correct this by fully-qualifying the constant:

    class MyClass
      include LowType
      class String; end
      def say_hello(greeting: MyClass::String); end
    end

    MyClass.new.say_hello(greeting: "hello") # => ArgumentTypeError
and you could imagine a Rubocop linter rule saying "you must use absolutely qualified constant references like `::MyClass::String` in all type annotations" to prevent a problem like this from happening if there does not end up being a way to solve it in the implementation.

Anyways, overall:

- I'm very impressed by the ingenuity of the approach

- I'm glad to see more interest in types in Ruby, both for runtime type checking and syntax explorations for type annotations

[1] https://codeberg.org/Iow/type/src/branch/main/lib/definition...

as opposed to whether the construct is allowed/part of the language

Arguably this is also semantics. Type checking and reporting type errors decides whether a construct is allowed or not, yet belongs squarely in the semantic analysis phase of a language (as opposed to the syntactic analysis phase).

how it differs from syntax

Consider a language like C, which allows code like this:

    if (condition) {
        doWhenTrue();
    }
And consider a language like Python, which allows code like this:
    if condition:
        doWhenTrue()
The syntax and the semantics are both different here.

The syntax is different: C requires parens around the condition, allows curly braces around the body, and requires `;` at the end of statements. Python allows but does not require parens around the condition, requires a `:`, requires indenting the body, and does not allow a `;` at the end of statements.

Also, the semantics are different: in C, `doWhenTrue()` only executes if `condition` either is a non-zero integer, or can be implicitly coerced to a non-zero integer.

In Python, `doWhenTrue` executes if `condition` is "truthy," which is defined as whether calling `condition.__bool__()` returns `True`. Values like `True`, non-zero numbers, non-empty containers, etc. are all truthy, which is far more values than in C.

But you could imagine a dialect of Python that used the exact same syntax from C, but the semantics from Python. e.g., a language where

    if (condition) {
        doWhenTrue();
    }
has the exact same meaning as the Python snippet above: that `doWhenTrue()` executes when `condition` is truthy, according to some internal `__bool__()` method.
Libghostty is coming 10 months ago

I think my problem is when I realize that I had unsaved changes open in a different neovim instance. If the file was not dirty in any other open neovim instances then I don't have the same problem.

Libghostty is coming 10 months ago

Do you consider yourself a neovim terminal power user?

I tried a while back to invert my workflow (from tmux driving neovim to neovim driving terminals) because I thought it might be easier to only ever have one buffer open for a given file, instead of attempting to open a file in a given pane only to realize that it's already open in a different neovim instance in a different pane.

When I was testing that stuff out I don't think I noticed particular issues with text reflow that would benefit from being solved by swapping to libghostty, rather my pain points were just about how to adjust to the different paradigm. I'd be curious to hear more about someone who is all in on Neovim embedded terminals (and possibly how libghostty might make it better).

Various apps already do this, if you find yourself reading lots of PDFs on phones. For example I use PDF Expert on iOS to do this. It’s not perfect—depending on the quality of the PDF there might be weird artifacts in the reflowed text (e.g., “ff” ligatures getting mapped to the Unicode ligature character “Latin Small Ligature Ff (U+FB00)” which breaks copy/paste/search).

But for PDFs which are really hard to read on a phone otherwise, it’s really a nice investment.

Very neat! I was delighted to see that "drag to side of screen" tiled the window using that half of the screen. Then I opened a new window, and I was (unreasonably) surprised to see that there wasn't a tiling window manager that put my second window in the other half of the screen.

This response article makes the opposite claim, that it was Derek Thompson who played fast and loose with sources. As evidence, the Stoller piece cites testimonials from both Lance Lambert and Luis Quintero saying that in their interviews with Thompson, they never went on record as repudiating claims made on the BIG newsletter.