IME this kind of "magic" very quickly loses its appeal when you have to debug it. The author's idea of libraries wrapping this stuff up so users don't have to care about it just doesn't pan out at all in my experience.
1,696 karma · joined December 9, 2019
IME this kind of "magic" very quickly loses its appeal when you have to debug it. The author's idea of libraries wrapping this stuff up so users don't have to care about it just doesn't pan out at all in my experience.
I never even mentioned anything that even relates to runtime types, I don't know why you'd bring them up?
And saying that classes, inheritance and overriding are orthogonal is bizarre. They're intricately linked and at the heart of OOP. It doesn't even make sense to define overriding without inheritance.
>In the end OO and FP are anyway the same thing at the core. You can mechanically translate one into the other:
Coinductive types have destructors so they look a bit OO-ish, but that's it. The key part of OO isn't having fields, but inheritance.
Inheritance is so nightmarish to me because people tend to use it in such a way that I often don't have the slightest clue what I'm actually dealing with, buried under layers of indirection that seem to serve absolutely no purpose. If I have an object of class X, is that actually an X, or something that just looks like it and actually behaves differently because someone decided to override a method in a subclass?
Interfaces tend to be much less monstrous IME, so I don't think the issue is with abstractions themselves. Especially when people use interfaces to abstract over common behavior between data types, rather than trying to future proof for something that never occurs.
Other than that, the difference is mainly in infrastructure/libraries and community.
>claiming that automobiles are really the same as bicycles, just faster. Except automobiles resulted in major shifts in both behavior and policy, and there’s a reason people don’t ride bicycles on the autobahn.
But speed (and weight) is the key difference. If cars were just really fast and heavy bikes, would anything change? I don't think so. It's purely quantitative, rather than qualitative.
It might be that the quantitative differences make generative systems harmful (though I'm not convinced), but that's not substantiated by claims about it not actually being learning.
As a more obvious example, if you want to modify a.b.c.d.e (which isn't unrealistic), you'll need to call the constructors of A, B, C and D. If you don't use lenses, this is the code that will be duplicated. You can spread it between the classes or do all that in a method of A, but if you want to also modify a.b.c.d.f, you'll need to duplicate all that code (add another method to A, B, C, D that each calls the constructor).
With lenses, you define once how to access d from a and then any modification of d can happen through that. If the structure changes, you only need to do the changes once by modifying the lens.
And you're close, it's about using immutability to make value equality cheap by making reference equality a proxy for it. If you don't use mutations, value equality implies reference equality and is thus equivalent (since reference equality already normally implies value equality). That means you can get away with just a single pointer comparison in comparison to completely traversing both structures. This is e.g. what React does to determine whether arguments of a component have changed.
(Well, at least I also struggle to think of a scenario where both types of equality are semantically important).
Lenses are "smart" in the sense that they only copy what's necessary.
Imo something like dall-e or midjourney is much worse.
Now, rewriting everything, including all the legacy drivers? Yeah, never happening even if Rust succeeds utterly.
[1] https://www.pcgamer.com/if-dwarf-fortress-sells-millions-on-...
Saying that it just looks like P(text|internet) is a tautology, it's a text predictor trained on the internet. This doesn't tell you anything about why phenomena like the above occur, or why it occurs only in large language models and only in some of them.
Due to being written in C++, mods are quite restricted in what they can do (especially in comparison to more recent games like Rimworld that are trivial to decompile due to using C#). Especially frustrating because performance really isn't up to par - a lot of the old generational forts from a decade+ ago are really only possible now if you tailor your gameplay to account for performance issues.
[1] https://en.wikipedia.org/wiki/Perverse_incentive#The_origina...
Inductive types are built using constructors. An example, defining two lists:
l1 = nil
l2 = cons 1 l1
Induction/recursion principles (fixpoints + pattern matching) consume them to produce some other value.
Examples:
length nil = 0
length (cons x xs) = 1 + length xs
For coinductive types it's the inverse. Coinduction/corecursion principles (cofixpoints + copattern matching) produce them. Take streams with the destructors hd : Stream A -> A and tail : Stream A -> Stream A). By defining how it's destructors work on the stream, we define a stream blink : (Bool -> Stream Nat):
hd (blink True) = 0
hd (blink False) = 1
tail (blink True) = blink False
tail (blink False) = blink True
blink True is the infinite stream 0101010101010101... To consume the stream, you call the destructors on it (and receive the defined result).
Now, just like how records are inductive types defined via destructors, the same can be done allowing coinductive types to be defined via constructors. In Coq this is called positive coinductive types and they're the original form of coinductive types introduced to Coq (and the only one until ~2019), but they're also not exactly well behaved, which is why it's now recommended to use the negative form, though e.g. this paper still uses the positive form. If you want to know more about this, I'd recommend reading the Coq documentation :-)
>Also curious how they interact with the termination checker
While infinite, negative coinductive types don't pose any termination issue because they're lazy - blink only 'describes how to deconstruct it' and does not do any computation of its own. So if you apply a finite amount of destructors to it, you only get a finite computation.
(positive coinductive types are a bit more tricky - there's a special productivity condition for them.)