justinpombrio | 4 months ago | on: Memory access is O(N^[1/3])
justinpombrio's comments
justinpombrio | 5 months ago | on: Why haven't local-first apps become popular?
Still, every algorithm that's actually labeled a CRDT shares a magical property: if my replica has some changes, and your replica has some changes, our replicas can share their changes with each other and each converge closer to the final state of the document, even if other people have been editing at the same time, and different subsets of their changes have been shared with you or I. That is, you can apply peoples' changes in any order and still get the same result. I don't think it's useful to call anything without that property a CRDT.
justinpombrio | 5 months ago | on: The Expression Problem and its solutions (2016)
Each data type is a `struct`. Each operation is a trait. You `impl` each trait on each struct.
This works even if you're using a library that has declared `struct A` and `struct B` and `trait F` and `trait G`, and you want to add both a `struct C` and a `trait H`, and fill out the whole 3x3 grid without modifying the library.
The library says:
struct A { ... }
struct B { ... }
trait F { ... }
impl F for A { ... }
impl F for B { ... }
trait G { ... }
impl G for A { ... }
impl G for B { ... }
fn some_function<T: F + G>(data: T) { ... }
Your code says: use library::{A, B, F, G};
struct C { ... }
impl F for C { ... }
impl G for C { ... }
trait H { ... }
impl H for A { ... }
impl H for B { ... }
impl H for C { ... }
fn another_function<T: F + G + H>(data: T);
Now `library::some_function()` can be called with an A, B, or C, even though C was defined by the user of the library. And `another_function()` can be called with A, B, or C, even though A was defined by the library.justinpombrio | 11 months ago | on: Show HN: I made a tool to port tweets to Bluesky mantaining their original date
<troll> The definition of dictionary is just "Words about words" (source: Urban Dictionary), so I'd say that Urban Dictionary qualifies. </troll>
justinpombrio | 1 year ago | on: What can be computed? A practical guide to the theory of computation (2018) [pdf]
Something that cannot be proven within which formal system? Every true statement can be proven by some formal system. No formal system can prove all true statements.
(This is all about Godel incompleteness. Turing incomputability does apply though, see the sibling comments! It's important to keep them separate, they're saying different things.)
justinpombrio | 1 year ago | on: What can be computed? A practical guide to the theory of computation (2018) [pdf]
Yes, that's correct, it's provably impossible to construct an LLM that, if you ask it "Will this program halt? <program>", answers correctly for all programs. Likewise humans, under the assumption that you can accurately simulate physics with a computer. (Note that QM isn't an obstacle, as you can emulate QM just fine with a classical computer with a mere exponential slowdown.)
justinpombrio | 1 year ago | on: What can be computed? A practical guide to the theory of computation (2018) [pdf]
Someone's going to think that, since you can formally model computer programs (and thus formally model how an LLMs runs), that means that Gödel's Incompleteness Theorem applies to computer programs and to LLMs. But that's irrelevant; being model-able doesn't make something a formal system!
"Formal system" has a very technical meaning: it has a set of consistent axioms, and the ability to enumerate formal proofs using those axioms. For example, Zermelo-Fraenkel set theory is a formal system [1]. It has nine formal axioms, which you can see listed in its Wikipedia article. Utterly different kind of thing than humans or LLMs. Comparing an LLM to ZFC is like comparing a particular watermelon to the number 4.
[1] https://en.wikipedia.org/wiki/Zermelo%E2%80%93Fraenkel_set_t...
justinpombrio | 1 year ago | on: Is this the simplest (and most surprising) sorting algorithm ever? (2021)
justinpombrio | 1 year ago | on: Is this the simplest (and most surprising) sorting algorithm ever? (2021)
justinpombrio | 1 year ago | on: Is this the simplest (and most surprising) sorting algorithm ever? (2021)
Sometimes it's more swaps, and sometimes it's less. The vague pattern looks like Can't Believe Sort does slightly fewer swaps on larger lists, but that could also have been noise.
Why do you expect them to have exactly the same number of swaps?
> Bubble sort can do less than this.
Oh sorry yeah, I implemented bubble sort without any sort of early return, for a more direct comparison.
justinpombrio | 1 year ago | on: Is this the simplest (and most surprising) sorting algorithm ever? (2021)
The number of comparisons is of course about twice as large, because Bubble Sort always does exactly n(n-1)/2 comparisons while Can't Believe sort always does exactly n*n comparisons.
https://play.rust-lang.org/?version=stable&mode=debug&editio...
justinpombrio | 1 year ago | on: Is this the simplest (and most surprising) sorting algorithm ever? (2021)
Could you link to what you're talking about? And what's its big-O runtime?
justinpombrio | 1 year ago | on: After 20 years, math couple solves major group theory problem
justinpombrio | 1 year ago | on: JJ Cheat Sheet
justinpombrio | 1 year ago | on: JJ Cheat Sheet
I like the idea of putting the edits on the arrows, but there are a couple senses in which the edit is associated with the change itself rather than an edge between two changes:
1. A change with two parents starts out by merging them, and then it can make edits on top of that merge. If the edits go on an edge instead of on a node, which of the two edges do those changes belong on?
2. If you move a change (e.g. by rebasing it), its diff comes with it. I guess you could say that when you rebase, you're not moving just the node but also the edge from it to its parent?
Even so, diffs on edges feels very right. I may update that diagram.
EDIT: Updated!
justinpombrio | 1 year ago | on: JJ Cheat Sheet
justinpombrio | 1 year ago | on: JJ Cheat Sheet
Unfortunately the arrows are kind of confusing regardless of which way they go. You're suggesting they point forward in time, from the old commit to the new commit. The way they're drawn is the direction of the reference: a commit points at its parent. The argument in favor of each way the arrows could go feel about equally strong to me, and my understanding is that the convention in repo diagrams is for arrows to go in the direction of the reference, so that's what I went with.
justinpombrio | 1 year ago | on: Tell HN: Cloudflare is blocking Pale Moon and other non-mainstream browsers
Consider messaging the owner to tell them you were trying to buy a product on their site and the site wouldn't let you. There's a chance that they'll care and be able to do something about it. But no chance if they don't know about the problem!
justinpombrio | 1 year ago | on: Jujutsu VCS: Introduction and patterns
The default for what's immutable is actually `main` together with untracked remote branches [1]. I agree that what you suggested should be the default though. You can change it by adding this line to your `jj` config:
[revset-aliases]
"immutable_heads()" = "builtin_immutable_heads() | remote_bookmarks()"
(To edit your jj config, run `jj config edit --user`.)[1] https://jj-vcs.github.io/jj/latest/config/#set-of-immutable-...
justinpombrio | 1 year ago | on: Life is more than an engineering problem