But people don't want to run an always-online server to send their stuff to peers, so they host it on the main bsky servers. The problem with p2p is UX; people don't want to DIY their server.
3,448 karma · joined May 23, 2020
But people don't want to run an always-online server to send their stuff to peers, so they host it on the main bsky servers. The problem with p2p is UX; people don't want to DIY their server.
It seems intuitive to me that the "hwaet" functioned more as a literary device than as a simple call for attention. It is, after all, a poem.
Of course, running an appview that indexes the whole world is expensive, and running an appview that doesn't index the whole world is less convenient than just using bluesky's appview, so the only people running an independent appview right now are blacksky.community. It isn't stable yet, but it works, and there are active users who are banned on bluesky: https://staging.blacksky.community/profile/did:plc:63hvnyjvq....
I'm optimistic that running independent appviews (especially appviews that index content lazily, on demand) will get easier in future.
Bluesky, though, is one big pool. There's no seam between servers. The experience is much nicer.
Also, bluesky caught on among my peers in a way that mastodon never did, which was always going to be the deciding factor. I imagine not having to "choose a server" was a big part of that.
I'd heard of beads as a lightweight issue tracker for agents, so this gave me a real shock. What could all that code POSSIBLY be doing? Going to the repo and poking around, I truly cannot tell. There's an enormous `docs/` folder with no hierarchy, containing files like `MULTI_REPO_HYDRATION.md`, which "describes the implementation of Task 3 from the multi-repo support feature (bd-307): the hydration layer that loads issues from multiple JSONL files into a unified SQLite database," and `ANTIVIRUS.md`, a 7KB text file about how `bd.exe` sometimes gets flagged as untrustworthy by antivirus software.
I opened a random go file, `detect_pollution.go`. This is a CLI command for detecting and cleaning up test tickets from a production database by (1) scanning ticket titles for testing-related prefixes like "debug," "test," or "benchmark," (2) scanning for short descriptions, (3) scanning for suspicious phrases like "sample ticket," and (4) scanning for batches of tickets that were created all at once. It uses these signals to compute a confidence score for each ticket that determines whether it should be deleted. This command was deprecated and replaced by `doctor_pollution.go`, which reimplements large parts of `detect_pollution.go` and is not, at a glance, substantially different. Two seconds of thought will tell you that this feature is unnecessary, since you can create tickets with a "#test" tag and then delete them by tag.
I don't want to come across as mean, but Steve should be embarrassed by this. It's grotesquely baroque and completely unmaintainable—proof positive that whatever he's doing isn't working.
Depends what they did. Let's check the forum post…
> they had created and promoted hate sites, photoshopped images, and harassment targeting limor, me, and others at adafruit. this was done on company time, shared, promoted.
Ok yeah if a company is sharing bigoted photoshops of my likeness, yes, I'm gonna demand that they discipline the responsible employees. Obviously.
I don't have any reference point for what "hate sites, photoshopped images, and harassment [meriting HR training]" constitute, but if it's anywhere near as bad as it sounds, sure, escalate the situation and air your dirty laundry in public. This is unacceptable behaviour, and apparently it went on for years. When you're being publicly harassed, you have no duty to indefinitely restrain your response to private, polite emails.
Backwards/forwards compatibility matters a lot across service boundaries. I'm not saying Protobuf needs to be exhaustively matchable. But native types from a static library? You want to be able to match those exhaustively.
To reiterate, starches have been a dietary staple in pretty much every society forever, whereas meat has been relatively expensive and rare. The obesity epidemic, which began in the 1970s, did not correspond with the invention of flour or rice.
> I would use this metric: could you conceivably produce this in an average kitchen with the raw materials?
This is completely arbitrary. Why is "milling grains into a fine dust" proscribed, when blending soup in an average kitchen's food processor is fine? We've had mills for millennia and food processors less than a century. Plenty of raw foods smell unpalatable; that doesn't mean the cooked version is secretly smelly or whatever. Besides, what is and isn't a "raw ingredient" is itself arbitrary. I can't make any type of vegetable oil at home, deodorized or otherwise—I don't have an oil press. I can make lard, but that doesn't mean it's better for me.
You're talking about the vibes given by various foods here, rather than their actual health effects.
Rust does it that way & has never trended in any other direction. The generally-accepted wisdom is "thiserror¹ for libraries, anyhow² for applications"—i.e., any error that's liable to be handled by a machine should be an enum of exhaustively-matchable error kinds, whereas anything that's just being propagated to the user should be stringified & decorated with context as it bubbles up.
Certainly, unified error types which wrap and erase more specific errors are sometimes desirable, but equally they often are not. Languages which support exhaustive matching support both, permitting us to choose based on context.
However, this definition of chemical modification doesn't really include hydrogenated vegetable oil. Industrial hydrogenation is done by raising oil to very high temperatures in the presence of a nickel catalyst & then adding hydrogen. We modify it on a chemical level, but primarily by heating it, not by adding reactive substances. And if that counts as chemical modification, then so does cooking!
Anyway, no—people generally used "processed" to describe a particular vibe they get from certain foodstuffs whose production seems too industrialized. There's no rigorous basis for determining what is and isn't "processed" because people use it to describe their feelings about food, not any underlying property of food.
If you search a simple question like "is bread processed," you get a bunch of articles saying "well, since there's no agreed-upon definition for processing and the definitions we do have aren't particularly clear, there's really no answer to the question. But don't worry, because (given the overwhelming vagueness of the category), it's also impossible to say whether processed foods as a category have any health implications, so you shouldn't worry about it."
The normal recommended daily intake for protein is 0.8 g/kg. 1.2-1.6 is silly; that's a recommendation for athletes.¹
Starches have been a dietary staple in pretty much every society forever. Sugars have not. It's silly that they treat grains as a "sometimes" food.
There's also the weird boogeyman of "processed food." Almost all food is processed to some degree & always has been. We've been cooking, baking, juicing, fermenting, chopping, grinding, mashing, etc. long enough that it influenced the shape of our teeth. Certainly we haven't been making Pizza Pockets that long, but the issue there isn't processing, it's ingredients. And the reason people buy Pizza Pockets isn't that they think they're healthy—it's that Pizza Pockets only need to be microwaved, and cooking a real meal takes time that a lot of people just don't have.
[1]: https://www.strongerbyscience.com/athlete-protein-intake/
let data = serialize(&doc)
.or_raise(|| StorageError::permanent("serialization failed"))?;
you're burying the error thrown by `serialize(&doc)` in such a way that you have to dig for it dynamically to recover it.The closure in `or_raise` should take the error from `serialize(&doc)` as an argument and save the actionable details. It makes sense to have an "outermost error" when you're talking about a context trace that provides information to humans, but an error which a machine responds to should be flat & statically matchable.
Something like this:
let data = serialize(&doc)
.or_raise(|e| Exn::with_message(
"serialization failed",
StorageError::fromInner(e)))?;
where `StorageError::fromInner` decides whether the error should be permanent or not based on the contents of `e`, and saves any details that would be relevant for automatically recovering from the error.I'm unconvinced. The process of songwriting is so dependent on being able to listen to what you've made and decide whether you enjoy it or not. We can train a model to imitate popular music, but we can't train a model to enjoy music, because we can't quantify enjoyment and turn it into a data set. You can train an LLM on soup recipes, but you can't train one to taste the soup and tell you whether it's good or not.
That's part of what offends me so much about the notion of AI-assisted "creativity." Creating music should be a way of engaging more deeply with music, but you've discovered a way to pay even less attention to music than before. None of the details in an AI-generated song really matter; they were chosen arbitrarily, because they seemed normal. Indeed, they are so normal that your ear will slide right off of them.
You can't fix a soup that someone else prepared—not as an amateur. You don't know what went into it, so you can't pick out the individual flavours and decide whether they're right or not. They were never "right" for you, because you didn't pick them. It's like ordering a Big Mac, taking it apart, and trying to workshop it into Duck à l'Orange. All you get is a Big Mac with some orange slices on it. Maybe you like Big Macs; maybe you're happy. It's a pretty poor substitute for creativity, though.
> "This system is unhackable, if the user doesn't do the thing that hacks it" is not very useful.
It's the best you're gonna get, bud. Nothing's "unhackable"—you just gotta make "the thing that hacks it" hard to do.
Err(report) => {
// For machines: find and handle the structured error
if let Some(err) = find_error::<StorageError>(&report) {
if err.status == ErrorStatus::Temporary {
return queue_for_retry(report);
}
return Err(map_to_http_status(err.kind));
}
They get it right elsewhere when they describe errors for machines as being "flat and actionable." `StorageError` is that, but the outer `Err(report)` is not. You shouldn't be guessing which types of error you might run into; you should be exhaustively enumerating them.I'd rather have something like this:
struct Exn<T> {
trace: Trace,
err: T,
}
impl<T> Exn<T> {
#[track_caller]
fn wrap<U: From<T>>(self, msg: String) -> Exn<U> {
Exn {
trace: self.trace.add_context(Location::caller(), msg),
err: self.err.into(),
}
}
}
That way your `err` field is always a structured error, but you still get a context trace. With a bit more tweaking, you can make the trace tree-shaped rather than linear, too, if you want.I think actionable error types need to be exhaustively matchable, at least for any Rust error that you expect a machine to be handling. Details a human is interested in can be preserved at each layer by the trace, while details the machine cares about will be pruned and reinterpreted at every layer, so the machine-readable info is kept flat, relevant, and matchable.
We've had LLMs for years. Image models and coding agents have gotten remarkably good, and their output is all over the place. So where is the AI writing? Outside of automated summaries, formulaic essays, and overly verbose LinkedIn posts, nowhere.
If you're looking for suggestions, I'd love for you to start with a problem that isn't trivially fixable.
---
> Only 13% preferred the AI oral format. 57% wanted traditional written exams. 83% found it more stressful.
> Here is an email from a student: "Just got done with my oral exam. [...] I honestly didn't feel comfortable with it at all. The voice you picked was so condescending that it actually dropped my confidence. [...] I don't know why but the agent was shouting at me."
> Student: "Can you repeat the question?" Agent: paraphrases the question in a subtly different way.
> Students would pause to think, and the agent would jump in with follow-up probes or worse: interpret the silence as confusion and move on.
---
Based on these highlights, you'd think the experiment was a wash. The author disagrees!
> But here's the thing: 70% agreed it tested their actual understanding: the highest-rated item.
Man, you could shoot me with a gun, then make me write an essay, & I'd be forced to agree that you had tested my "actual understanding." That doesn't mean my performance wouldn't suffer. Also, 70% is not very high. That's barely two thirds.
Even the grading was done by LLMs (rather than having a TA grade a transcript, and the results were lower. The author defends this by saying, "Students will be evaluated outside the university, and the world is not known for grade inflation," but the world isn't "known for grade inflation" because it doesn't grade you at all. That's not even an excuse, it's just nonsense. It'll toughen you up, or whatever. Was this post written by an LLM too?
> Take-home exams are dead. Reverting to pen-and-paper exams in the classroom feels like a regression.
"Regression"? I mostly wrote pen & paper exams, and I only graduated a few years ago. If students want more flexibility, team up with other courses to supervise multiple exam sessions. Leaked questions aren't going to be any more of a problem than it was for take-home exams, especially since they can't take the booklets with them when they go.
It sounds like these students had a terrible time, and for what? Written exams work fine. These guys just wanted to play with LLMs.
It's true that you don't see stuff like arena allocators very often, but Rust's lifetime/ownership semantics mean it's often best for related pieces of data to be owned by a single parent, which unifies their lifetimes such that they all start and stop being valid at the same time. I suppose that's only part of the equation, since an arena has you allocate everything at once in a continuous block, which has performance advantages.
Still, I can't help but see an underlying conceptual symmetry there. I feel like the scoped arena school of memory management would be nicely complemented by compiler lifetime analysis. Arenas definitely simplify lifetime issues, but that feels like an opportunity to experiment with additional static compiler checks, not a reason to eschew them. I'd love to see a language which required explicit deallocation but enforced it with linear types.
I used to be an "initialize everything" partisan, but meaningful zero-values have grown on me. I still don't think everything should be zero-initialized, though (that is, valid when initialized to zero). I'd prefer it if there were a two-colour system where a type is only zero-initialized if it don't contain a pointer, either directly or transiently.
The trick would be that zero-initialized sum types have a default variant, and only that variant needs to be zero-initialized. So a type that requires explicit initialization can be made zero-initialized by wrapping it with Optional<T>, whose default value is the zero-initialized None value. So even though you end up with coloured data types, they're easily contained & they do not spread virally.
I think this offers the best of both worlds. It gives you explicit nullability while still permitting something like `make([]SomeStruct, n)` to return a big block of zeroes.
> Languages like Rust were designed from day zero around explicit individual-element based initialization
> Ownership is a constant concern and mental overhead when thinking in an individual-element mindset. Whilst ownership is obvious (and usually trivial) in the vast majority (99+%) of cases when you are in the grouped-element mindset.
I think there's a sort of convergence here, because one of the tips that's often recommended for dealing with lifetimes issues in Rust (e.g. cyclic data structures) is grouping elements together in a parent structure and referring to them with handles instead of references. Often this is done with integer handles in a simple Vec<T>, but libraries like slotmap exist which make that pattern safer & more ergonomic, if desired. The language features of Rust naturally encourage grouped-element thinking by making individual-element strategies high friction. In fact, I wouldn't be surprised if a reliance on individual-element strategies were part of the reason so many people struggle with Rust when they're first learning the language.
Ownership and lifetimes are a source of essential complexity in programming. I don't see any contradiction between managing them with a grouped-element mindset and managing them with language features; in fact, I think the two go hand-in-hand quite nicely.
A program that never asserts its invariants is much more likely to be a program that breaks those invariants than a program that probably doesn't.
I think the key difference between error values and checked exceptions is that functions in an error-value language tend to fully own their error types. With checked exceptions, if foo() calls bar() and then bar's implementation changes to add a new exception, suddenly foo is throwing stuff it didn't know about. With error values, foo is never simply throwing whatever bar throws, because foo has to explicitly transform bar's error value into its own error type if it wants to bubble it up.
It's a subtle change—perhaps a purely cultural change, even—but the difference is obvious if you picture a Java method assuming responsibility for its errors the way Rust functions do. Just imagine someone writing `try { bar(); } catch (BarError f) { raise FooError::fromBarError(f); }`. It's weird. It isn't done. But `bar().map_err(|e| FooError::fromBarError(e))?` is completely idiomatic in Rust.
It helps that defining a new type is low-ceremony in Rust, whereas it's high-ceremony in Java. I think that has a lot of benefits. When we model everything with types, it's important that types be cheap.