HNHacker News
TopNewBestAskShowJobs

Twey

584 karma · joined January 30, 2011

submissionscomments
Twey··on What About Rails?
I think LLM chatbots are great for discovery. Better than CLIs, better than WIMPs. Where previously you'd go to a search engine to figure out what command to run or where to find that obscure tickbox, now the search is built right into the interface, and you can give it arbitrarily complex queries in natural language. It turns out there are a huge number of products for which discovery is the bottleneck.

The problem with chatbots is that they don't scale very well with you. A proficient user of your application knows where that tickbox is and can get to it in three clicks (or if you have shortcuts, one keyboard chord). But the chatbot still makes you type a whole (ish) sentence. If that's something you want to do 3 times a minute that application is now unusable.

Twey··on What About Rails?
For sure! One of the big draws of malleability is that you can adapt it to your particular needs. Natural language is a UI just as much as a WIMP GUI, and for some tasks or some people it's a great one.
Twey··on What About Rails?
Yes you caught me out in a bit of exaggeration there. For the topmost level of tasks people typically do want a low-latency, high-bandwidth UI for displaying and manipulating the information they care about.
Twey··on What About Rails?
In case it wasn't clear, that was intended as a somewhat satirical representation of the position I was discussing :) An important part of any ‘universal UI’ — and arguably the hardest part of building it — has to be that it allows feedback from the API about what and how to display, which is always in tension with the malleability/universality aspect. People are trying to solve that for chatbots right now, with limited success so far.
Twey··on What About Rails?
I think the popularity of these chat interfaces disagrees with that to some extent. We do already have malleable software for people who can program — the problem has always been bringing it to everyone else.
Twey··on What About Rails?
As a developer/user I hope for that outcome too — if everybody is using your app through an agent you might as well cut the maintenance cost and drop the GUI, or at least have a nice API alongside it to make it nicer for the agent. But I wonder what it does to the financial incentives to produce software.
Twey··on What About Rails?
> 37signals differentiates their products with opinionated UI/UX, not novel features. They are rewriting Hey as six native apps because the web fidelity isn’t good enough. So UI matters enough to justify complete rewrites, but also everyone just wants CLIs?

I've seen this one a few times lately. People should stop building UIs, nobody wants to interact with a UI. Everyone's app should just be an API that you can use with a chatbot. Except for my app — my app is a handcrafted miracle of artisanal UX and its UI will change the way you see the world.

It's exactly the old argument, just now with LLMs in the place of shell pipelines: in terms of functionality and value to users, software ought to be malleable and composable. We've known it since the eighties. But the model of selling a piece of software as a product as if it were a pair of shoes is incompatible with that. You need a big monolithic application to justify users paying a bunch of money for it, and you need it to have a fancy interface that makes an impression. And the whole software industry is built on top of that model. Where monolithic software is completely unfit for a purpose, like when it needs to be a component of a larger system, we rely on (mostly unpaid) OSS.

Except now LLMs let you, with very little technical know-how, plaster a ‘programmable’ interface on top of unstructured data/interface meant for humans, and because that's what we actually want, of course everybody does that. So the end result is a wildly expensive pipeline from API to UI and back to API again. I wonder how long the legacy ‘human-oriented’ layer in the middle, and the industry that's been built on top of it, will last.

(Separately, chatbots are not great as a UI for most things, and the problem of building the universal UI still also stands. But it turns out for a lot of things people would rather have a bad universal UI than a good special-purpose UI for each task.)

Twey··on Simple Is Not Small
> I guess only I'd disagree here that the additional assumptions are "fairly onerous" - but that's just an opinion. You may be doing work that butts into those assumptions far more often than I do (which is almost never).

I originally meant this mostly in the sense that it forbids a lot of values. But I do also think this is a bit of a Blub situation. There's a good class of pure data algorithms where basically everything you want to interact with is data, and if you're working in that space it's pretty great. But having spent some time in resource-typed languages, for any situation where I have to work with external resources or effects that can't be undone working in a language that doesn't have resource types and deterministic constructors really feels like a big step backwards. There are so many invariants you can't capture if you can't reason about value invalidation.

Twey··on Simple Is Not Small
> In Clojure the data is thrown away too, just at a slightly later point. The data is parsed then destructured then processed, and it is at the destructuring stage that irrelevant data is discarded and marked for GC.

Right; the difference is that by the destructing time you have lost the type information about that ‘discarded’ data, meaning you no longer know how to discard it. In Clojure this is not a problem, because the only values you're allowed to have are those that can be garbage collected, and (furthermore) garbage collected by calling a known function with nothing more than a universal set of GC metadata that is attached at a standard location on every value. But this is only possible because of a large restriction Clojure places on allowable values: the base ‘unitype’ of all Clojure values already contains all of this extra data, and any values for which it can't be attached or it doesn't make sense are simply forbidden.

> But you have to create and implement those traits. I'm not saying this is impossible in Rust; just a lot more onerous because you don't get all the machinery Clojure has that makes it trivial.

On this I absolutely agree. The only thing I'd be careful to distinguish is not simply that Rust doesn't have the machinery but that the machinery makes sense only given a fairly onerous set of additional assumptions about your values that Rust doesn't make (and that are usually held to be incompatible with a systems programming language, but IMO are also helpful for many — though not all — ‘userland’ programs).

> When I say "type", I mean a set of possible values. I don't think this is an uncommon definition; the first sentence of the "data type" Wikipedia page pretty much defines it the same way.

It's not an uncommon one, indeed, and while there are more sophisticated definitions (e.g. including equality) I don't think they need to be invoked for this discussion.

> Given this definition, you perhaps see why we can narrow a runtime type by composing it with a predicate.

No, you're right: I think I was thrown off by your assertion that these are uncoupled from the type system when we were talking originally of ‘conceptual’ types.

> My understanding is that you view types differently, as more of an interpretation of some sequence of bits, rather than a set of data values. Is that correct?

Quite the opposite. These words aren't entirely standardized so I set out my definitions above:

- I am using ‘value’ to refer to any object of discussion in a programming language that nominally exists at runtime; effectively, a value can be defined by the set of operations available on it - I am using ‘data’ to refer to values that have ‘data-like’ semantics, i.e. has ‘default’ move and discard mechanism like bitwise copy/overwrite or GC

Twey··on Simple Is Not Small
> Perhaps we only want to find patients with WBC counts outside a certain range, and discard those lines where the data is invalid. In which case, we could write a narrower type, and simply not parse the CSV rows that are invalid:

These two types aren't typing the same data, though: in the latter case (in Rust) you've actually thrown away some of the data, and the type reflects that. In some languages like Clojure or TypeScript you can't distinguish that from the case where you haven't actually thrown away the data and are secretly carrying it around as well as whatever data is explicitly typed there, but if you want to get it back you have to know that it was once there, i.e. have additional (conceptual) type information that you chose not to write down.

> Granted, but Clojure's approach can result in greater decoupling with less effort, particularly when dealing with imperfect data.

I think this isn't a great comparison: the usual vehicle for this thing in Rust is the trait, which manages to abstract over the representation statically without introducing a bunch of runtime machinery for it (though you can opt in to the machinery by using a trait object!). That's very idiomatic in Rust, although the trait syntax is a bit noisier (because it's more general).

> the entire problem of how we represent this data in memory is sidestepped.

It's not sidestepped — the language just makes an opinionated choice for you, which you can't avoid.

> these schema definitions are independent of the type system (i.e. decoupled), so we can apply them selectively or not at all

These are just predicates; I don't think they have much to do with this discussion? They don't help you to type your data: you (the programmer) still have to carry that type information around in your head in order to work with the values even after they are validated.

> Could you do the same in Rust or other similar languages? Sure, it's ultimately just maps and predicates. But many languages aren't geared up to make that pleasant to use.

There are good reasons for that, though. Nominal typing allows you to express and enforce constraints that aren't necessarily enforced by the structure of the type, at the cost of some extra ceremony. And systems languages often want to be able to express types that cannot be treated this way: types in which there is no sensible way to ‘ignore’ a value. Clojure makes some things easier to write by forcing you to carry around a bunch of extra stuff (both runtime data and static semantics) with all your values; that's a perfectly valid choice that makes an important subset of programs nicer to write, but it also excludes values that don't fit into that category or can't reasonably be coupled to their RTTI.

As we get better at writing compilers we're increasingly seeing a move towards mechanisms like Rust's traits or C++'s concepts that allow the programmer to actually abstract over the representation of a type (as opposed to forcing a uniform representation as is common in dynamically typed languages). If you do that well you can get the best of both worlds semantically, but these kinds of systems languages will always be a bit heavier syntactically because the syntax needs to support a wider range of types.

Twey··on Simple Is Not Small
> Then at which point does a type become equivalent to a restriction that could be decoupled from the underlying type by choosing a broader type?

I'm not totally sure how to interpret this question. If I already have a type that includes all the values I want as a subset, I can restrict that type by limiting the values it can take and restricting or removing the operations on it to guarantee they never produce any of the forbidden values. That's the basis of refinement type systems like Liquid Haskell etc. For any type I can describe this way I can also build it ‘from the ground up’ by starting with the empty type and adding operations, though it might not be as convenient. But the converse isn't true: not every type I can build additively can be refined subtractively from another type. For a start, you need to have a broader type to begin with, so that has to already be built somehow: you can't refine an 8-bit integer into an HTTP server.

> You might have a structure that has a WbcCount but not a BirthDate, or one with a BirthDate but not a WbcCount, or one with neither.

Remember that a `Box<dyn Any>` could also be a ‘no value’ type like `()` or a ‘maybe no value’ type like `Option<Date>`. But yes, there are many equivalent ways to write it depending on how likely the values are to exist; the precise formulation is just a question of ergonomics.

> We'd then be faced with the challenge of creating a type that could contain an arbitrary number of unique structs, and to be able to pull a struct out of that set by its type.

By its name, but yes, that's exactly what you get with the `other_fields` field in my example above.

> and discard all static typing

Well, that's not quite true: we discard (syntactically, but not conceptually!) the type information about the fields of this struct, but we can regain it later (from the conceptual information we retain) by trying to downcast the `Any`. Then you get your static type information back and the compiler can help you again.

> But both of those options are difficult or unidiomatic to write.

Sorry, I hope I haven't come across too harsh to Clojure here. As I thought I was clear about above, Clojure's ergonomics for writing dynamically-typed code are vastly superior to Rust's. By coupling runtime type information to values and restricting itself to only being able to deal with values that are data and have runtime type data attached, Clojure gets to make a whole bunch of simplifying assumptions that reduce the work the programmer has to do when dealing with such values. That's the trade-off: Rust chooses to be able to express a much wider range of types by not requiring that coupling, but in exchange you have to be much more explicit when you do want to couple runtime type information to your values. Some languages like C# aim to be able to do both, i.e. start strongly typed and drop down ergonomically into dynamic typing as desired, but Rust is not one of those languages.

> If we don't need to know the birthdate to calculate the white blood cell count, then we can exclude that field from the input type checking.

But that very exclusion requires that you know enough about the value to know that it can be safely ‘excluded’ without having any additional information about it. Specifically you need to know:

- how to access a subfield of the type regardless of the other fields on it — this requires that it contain RTTI

- how to discard the value — this requires that it have a destructor with a known calling convention

- how to move/copy the value into the function (and maybe even move out of the function if you return it or imperatively add it to some global state) — this requires that you know a ‘moving constructor’ for the value that can be used to safely move it to another location

In the Rust case you need to write down these things explicitly so the compiler can make sure you don't pass a value that doesn't satisfy them. In Clojure, the reason you don't need to write them down is that every value in the language is restricted to only be able to represent such values, so those constraints are implicitly on every function that you write.

Twey··on Simple Is Not Small
> You appear to be saying that the values of a type cannot be a subset of another type; that is, there is no '1', only a '1' that is an integer, a '1' that is a short, and so forth, and every '1' is distinct.

That's true, and you can prove it simply by observing that they react differently to ‘the same’ operations, e.g. (2¹⁶ - 1) + 1 has a different value (that responds differently to tests like `≥ 0`) depending on which type we're talking about. There's an injection into the larger type (a type coercion) that is very well-behaved, but it's not an identity map — it changes the operations on the value.

> Further, what's the practical difference between a type defined as consisting of the numbers [1 2 3], and an integer that's restricted to those values?

Nothing (ish: you have to be careful to describe what happens to all the integer operations when restricted to your type) — that's a totally valid way to define a type. But it's not the only way to define a type, because the world of types is much bigger than the world of restricted sets of integers.

> I need to ensure that a patient has some birthdate that's an anticipated type

No, you just have to be explicit about the possibility of a birthdate (or other fields) being of a wide type, e.g.

    struct Patient {
      wbc_count: Vec<u64>,
      birth_date: Box<dyn Any>,
      other_fields: HashMap<String, Box<dyn Any>>,
    }

Clojure just attaches that by default to every value.
Twey··on Simple Is Not Small
> But a value can have more than one possible type. The number 1 could come from an unsigned byte, or a signed long, for example.

Here we disagree. Unsigned 8-bit integers† and signed 64-bit integers, while both conveniently notated with Arabic numerals, are actually different values that support different operations, for example negation. They share quite a few similarities in how their operations interact with one another, for example each (assuming wrapping) is a monoid with 0 and +, but the semantics of the actual operations differs if looked at more closely. Mathematics agrees: the element ‘1’ of N/2⁸ and the element ‘1’ of Z/2⁶⁴ are not the same thing (their encodings coincide sometimes, but it's poor form to make assumptions about it).

† Bytes are data but not numbers, and so support only data operations like duplication and discarding, not number operations like adding and multiplication: as an artefact of representation you can usually ask the hardware or programming language to manipulate them as if they were numbers, but the result remains meaningless.

> The point is that you may not have anticipated that not everyone would know their date of birth, and the data you receive is invalid according to your earlier assumptions.

But that's exactly what I'm saying: you must have made some assumptions about that missing data, otherwise there is no safe thing you can do to it (including discarding it, which is a popular choice). This works in Clojure only because Clojure couples some semantics (data semantics plus operations on runtime type information) into every value, i.e. it restricts what values are even representable in the language in order to ensure that this function will always be safe to write.

In more strictly typed languages you can still talk about values that support these behaviours, but you are required to be explicit about it, because there are some values that can be represented that don't support these operations.

> Functions that don't require the date of birth will continue to work with no change required.

Any function that takes the date of birth ’requires’ the date of birth (or more generally a possibly-empty set of ‘leftover’ values). The only thing that differs is what's required from it: some functions might require that it be a date while other functions only require that it be data, for example. The universally imposed limitation that all values must be coupled to data semantics and runtime type information is convenient for ergonomics if you write a lot of these functions (since you don't have to remember to write that assumption down), but it's important to remember that it is a coupling — the resulting values are more complex than values without those things bundled on, and the trade-off is that you can no longer talk about values for which they don't hold.

Twey··on Simple Is Not Small
> I think you're using 'value' to mean something slightly different to the way I meant it. So to get us onto the same page, by 'value', I mean something independent of how its stored; that is, the number 1 is a value, whether it's stored as as 00000001 or 0000000000000001.

I'm using it in its most general sense: as an object of discourse in a programming language, independent of representation or semantics. 1 is a value in most languages, to be sure, but it's also specific type of value, viz. a number: you can do number things to it, like add it or divide it, or check it for equality with 2.

> A type limits both which values it is possible to represent (i.e. a u8 limits us to representing the integers 0 to 255), and also determines how it is encoded in memory (in this case 8 bits).

To reiterate: a type doesn't limit the values but specifies the values (or rather, more generally, the meaningful operations on a value of that type). Values are not numbers by default; only by being typed as a number does a value take on number semantics. Without the knowledge that a value is a number it is meaningless to treat it as a number.

> Yes, but ultimately the compiler needs to be able to map a sequence of bits to the value it represents

Sure; in any language implementation you have to represent the values somehow, nobody could disagree. My point is that the type doesn't (necessarily) specify that representation in any language I can think of.

> That's a common assumption, but the reality can be much more messy. It might be that we expect an integer between 5 and 100, but receive a string of UTF-8 characters instead.

Sure: that's not fundamentally different from the first example I gave. The real type of `:patient/birthdate` there is just the (discriminated) union of the date type and the string type. It still has a type, and if it didn't you wouldn't be able to process it (definitionally, because a type tells you what kind of processing makes sense for the value). And the string values aren't ‘outside’ the type: even if you choose to write the wrong type down in your Clojure, the fact that you also process strings means that you know the real type (and you embed that knowledge into the code).

Twey··on Simple Is Not Small
In fact the opposite is true: raw values have no valid operations and types allow you to add operations that make sense for those values. It's an unfortunate historical accident, which we're slowly getting over, that we conflate values with data (in the sense of ‘plain old data’, i.e. values that support a special hardware-supported kind of copying and moving, et cetera) and data with numbers, and then think we have to use types to restrict it from being treated as numbers and get back to smaller sets of values.

> In this sense, most statically-typed languages conflate how data is structured with how it is restricted.

Most statically-typed languages are actually very loose about how values are structured at runtime, leaving it mostly up to the implementation (e.g. see C++ padding and field reordering, or Haskell's autoboxing, which makes approximately no guarantees about what's behind the pointer — usually some graph-rewriting metadata). Where the conflation does exist is that a lot of systems languages allow you to write and typecheck code that assumes something about the language's representation of the type's values (e.g. that you can take the address of a field of a struct and later dereference it), though you can usually opt out of that with PIMPL or a trait object or something. But guaranteeing (and allowing the programmer to rely on the guarantee) that every value's representation also carries a bunch of additional runtime information is a much stronger version of that coupling.

> This can be useful when dealing with data that is in some sense invalid. You might receive data that's outside expected bounds or even of a different type, and it might make sense to handle it in some fashion.

The very fact that you can handle that data at all means that the value carries additional type information that allows you to do so. It's only ‘decoupled’ from the type in the sense that you didn't have to write it there, because it's automatically coupled to every value representable in the language regardless of what type you give it.

> This is a common necessity in pharmaceutical trials, for example.

I'll have to do some guesswork here, but I imagine when people make arguments like this they are significantly imagining a situation in which, say, all the values are expected to be in the range [5, 100] and some befuddled experimenter or piece of machinery gives you the value 2. A-ha, you say: I know sometimes the equipment undermeasures near the bottom of its range, so I'll clamp this value to 5!

This isn't really a type error. The fact you know you can safely do that means that the data is really typed in a different range than you said — but it's still typed. The conceptual type (even if you never write it down) is inherent in the very fact that you can somehow handle it: you know what to do with values down to 2 so the real type of supported inputs is at least [2, 100] (with some special semantics for the low values beyond that of being numbers).

A real type error looks like: you're expecting values in [5, 100] and then one of the values actually turns out to be the concept of intellectual honesty. That type doesn't support ~any of the same operations as the numbers you were expecting, even with the extended domain that more accurately reflects the set of values you can really accept. Even discarding it might have disastrous results for your experiment! In fact, the concept of intellectual honesty doesn't even have a good discriminator: while I know that's what you got because I'm the rascal who snuck it in there, you have no idea what it is, and no way of finding out. Most likely you're going to try to compare it to 5 to see if it needs to be clamped, with unpleasant consequences for us all.

Twey··on Simple Is Not Small
I think I struggle to assemble a coherent notion of what it means to (conceptually) decouple a value from its type. You can completely forget the type of a value and treat it as opaque bytes, but then there are no valid operations left on the value. Even moving it around or discarding it may be invalid if it's pointed to elsewhence. The only thing you can meaningfully do is try to recover its (static or dynamic) type information from somewhere and re-‘couple’ it.
Twey··on Programming is Art
Or here: https://this-museum-is-not-obsolete.com/
Twey··on Simple Is Not Small
> The reason for this is that in Rust, a struct couples type-checking to a fixed data representation. You can't get one without the other.

> Clojure decouples data representations from type checking.

This is funny to me because seen from the other side, (this) Clojure couples runtime type information to data structures: you're no longer allowed to define a data structure that doesn't have some runtime type information attached. A fixed static structure is just the consequence of not adding dynamic type information.

Meanwhile in Rust you can get type-checking ‘without’ a fixed structure by using trait objects.

Twey··on How Universities Should Prepare Founders
The libertarian take is surely exactly this, with the elitism either stripped out or pushed into the background (depending on your political bent): rather than employees and employers everyone should be a founder of their one-person company that raises money to do things that align with the interests of other individual founder-investors.

Mind you, I'm not totally sure how this differs from employer/employee relationships in practice except I suppose that each side has less contractual protection from the other.

Twey··on Zig’s Io.Threaded is neat
> in Clang/GCC the `__` is not explicitly reserved for builtins

In fact in C identifiers starting with _ are reserved for the implementation (except for in local scope if the second character is not an underscore or capital letter).

Twey··on Anthropic's 'watermark' text adulteration in Claude is a perversion of writing
People are very upset, especially in the arts, that Anthropic is changing the text to watermark it, but isn't that missing the point a little bit? They're not changing _your_ text whose every word you've carefully chosen for the exact effect, they're changing text that they're generating, i.e. text you've already chosen to give up control over. LLMs can't understand emotional nuance anyway.

The phrasing of the announcement implying that phrasing and diction don't change the meaning of text is insultingly dismissive of the whole field of literature, and I can see why people might take it as an afront, but the actual technology shouldn't have a negative impact as far as I can see. It seems to me that this one is more of a PR problem than something with real-world impact.

Twey··on Worms: The Future of Yesterday's Worms Today
You might enjoy Liero (or its modern descendent OpenLieroX) which is similar to Worms but with real-time play (and kept the pixel vibes).
Twey··on Show HN: Fuse – statically typed functional programming language
Right, so the definition of higher-kinded types is that the _parameter_ to the trait (here, `Self`) is higher-kinded (here, `* → *`) not that the trait is parameterized. So `impl Functor for List[A]` is not (semantically) correct: it's not `List[A]` that implements `Functor` but `List` itself.†‡ The important thing you get out of that is precisely the ability to talk about these kinds of universals: you can associate items to the type constructor itself before its eventual parameter is even in scope, and so the value of all such things must be the same independent of the parameter ‘for free’. As soon as you introduce the type parameter you incur a proof burden if you want to claim that the associated item is the same regardless of the value of the parameter, because you have introduced the syntactic possibility that they could vary.

† There's an encoding of higher-kinded types in some languages that don't really have them as first-class citizens (e.g. Rust with associated type constructors) that does this by adding a ‘rewrap’ item to the trait: you implement `Functor` for `List<A>`, but also (as part of the trait) includes a type constructor `Rewrap<B> = List<B>`. This lets you encode the fact that the `Functor` instance is defined for `List<A>` for all values of `A`, but you still struggle to prove that some of their items are independent of the choice of `A`.

‡ To see both of these side-by-side, consider the instance for pairs, which are functorial in their right parameter (as well as the left parameter: they are bifunctorial, but that's not relevant here). So if you have a curried pair type constructor `Pair : * → * → *` it really is true that `Pair[A]`, not `Pair`, is a `Functor`:

    impl Functor for Pair[A]:
        fun fmap[B, C](f: B -> C, self: Pair[A][B]) -> Pair[B][C]):
            Pair(self.0, f(self.1))
Twey··on Show HN: Fuse – statically typed functional programming language
Right, I got the notion — but syntactically I expect `Self` to refer to the thing named at the top of the block, which is a `Functor[A]`.

I think what both I and the sibling comment are getting at is that there is a difference between `Functor : (Self : * → *) → Class` and `Functor : (Self : * → *) → (A : *) → Class`/preapplied `Functor : (Self : *) → Class` and the syntax seems to merge the two (using syntax for the latter that is automatically abstracted to the former). But it's not clear to me that you can do that without losing the ability to express some things. The associated type is a pointed example because the unwanted dependence breaks type equality, but consider also an associated function that should _not_ be parameterized by `A`.

Twey··on Show HN: Fuse – statically typed functional programming language
Love to see a real-world example of GRIN!

    trait Functor[A]:
      fun map[B](self, f: A -> B) -> Self[B];
This looks a little wacky to me. I see that you can write HKTs in their η-long form and refer to them unapplied (`Functor`). But I don't understand how I would use this syntax to attach something to the trait that _doesn't_ depend on `A`. For (a silly) example,

    trait SizedFunctor[A]: Functor[A]:
      type Size;
      fun size(self) -> Size;
How do I know that `List[A]::Size` is the same type as `List[B]::Size`?

Relatedly, I want to read `Self` in there as ‘the thing that implements `Functor[A]`’ (e.g. List[A]`), but that makes `Self[B]`, instantiated, mean `List[A][B]`, which I think should be a kind error.

Twey··on Mathematics Without Mathematicians
I'd go even further: even the ‘cognitive elite’ (the university educated? researchers?) understand only a thin spike of frontier human knowledge, even if they might understand it very deeply. Outside their specialty and maybe a slightly raised background level of general education, the world is still magic. No one human understands even a significant chunk of human frontier knowledge. That's why we build artificial societies comprising many humans to make complex decisions.

Illustrations: https://matt.might.net/articles/phd-school-in-pictures/

Twey··on Mathematics Without Mathematicians
> Those "demons" didn't eat our lunch?

Tell that to the Europeans who were so afraid of naming the bear that its original name is still lost to us :) The Luddites also had a lot to say about the mechanical demons that were eating their lunches.

> Cognition is in the process of taking the leap from glacially slow biology under blind evolution, to lightening fast (scales of software in days, hardware in months) engineered self-optimization.

Yes-but, time scales are relevant only relative to other time scales (or the Planck time, if we get there). If the whole universe were to start running at 1,000,000× speed, you wouldn't notice. Speed is important to us humans currently because of our individual perspective and associated limited lifespan, but not in a universal sense (modulo some big hurdles like star lifetime and heat death). There are already self-replicating organisms with much faster iteration cycles than humans or human technology, like bacteria.

> Within 10 years, the robotics side will catch up with the speed of the software side. Biological cognition, bodies and economic status are all getting revolutionized within the next 10-15 years.

Just like it was in the 1980s, or the 1800s? I don't claim that these things _won't_ happen in the next ten years, but looking at it empirically people thinking that they will coïncides with every new technology and is a poor predictor.

> If there is reason in the short term to be discouraged, it is due to ourselves. But pushing for more ethical systems, hard, right now, is a better and right response.

I don't disagree, but it's not like we haven't been trying. Attempts to codify ethics go back about as far as human civilization, and it's not obvious to me that there's any faster road, and certainly not one fast enough to outpace our current rate of technological development (before you even start talking about AGI!).

Twey··on Mathematics Without Mathematicians
> The result is that we will live in a demon-haunted world, full of marvelous devices whose operation we will not understand, based on engineering principles we will not understand, discovered using formalisms we will not understand.

Well, that's just where we started — and then we developed science and mathematics to understand it. Why would it be any different if the demons live in our technological progeny instead of the natural world?

Twey··on Computation as a universal and fundamental concept
In constructive mathematics I think you'd be right: to call a problem decidable would require you to produce the algorithm that decides it, so you couldn't call the Collatz conjecture decidable (nor could you call it undecidable!). But the usual definition of decidability is classical.

This is slightly bizarre now I think about it: the definition of decidability allows the algorithm selection to be undecidable!

Twey··on Count Binface
We have official government ID (the national insurance number, roughly equivalent to a USian SSN), but names aren't expected to reliably link it in one hop, and we're politically averse to laws that require citizens to produce it in various circumstances, e.g. carrying ID cards, even though in practice most people do carry some form of government-sanctioned ID. Name changing is mostly limited by the fact that it becomes the duty of the person using a different name to laboriously link the new name to any previous names they've used wherever you need some paperwork in a different name. E.g. you need to provide your NI number to be employed, which typically means you need to have a paper trail back to it across any renames you might have had.
Page 1 of 9Next →