David Young

1.8K posts

David Young

David Young

@roboguy20

Computer science PhD student focusing in programming languages

Katılım Mart 2020
1.8K Takip Edilen573 Takipçiler
David Young
David Young@roboguy20·
@phone3301 This is probably not the kind of thing you have in mind, but that immediately makes me think of Jean-Yves Girard’s (a prominent, influential logician) mustard watch paper
David Young tweet media
English
1
0
2
73
David Young
David Young@roboguy20·
@haskell_cat but also what do I mean by "suggest" a decision procedure? I don't fully know a good way to describe that, I guess, hah. It's not quite a decision procedure on its own. It's sort of a spec for one, but it's a more "algorithmic" spec than the original description
English
0
0
1
17
David Young
David Young@roboguy20·
Hah, I was thinking about that, too! I was describing the problem to some of my non-PL friends, and I was trying to think of a good way to say that. I guess the most important thing is that it should suggest some actual decision procedure for the property. But I could also imagine things like that which are pretty messy, and probably wouldn't fit the intent...
English
1
0
1
40
David Young
David Young@roboguy20·
Here's an interesting math puzzle I recently saw from McBride (slightly reframed): Take some arbitrary set (or type) X, and consider lists of lists of X elements ("lilists"). We can take one of these and flatten it into a list of X elements. Next we define a relation `<->`, where two lilists `xss` and `yss` are related if and only if they have the same flattening (which we write as `xss <-> yss`). For example, maybe X is the set of integers and we have `[[1], [2,3], [4]] <-> [[1, 2], [3, 4]]` because the two lists have the same flattening. We can also take our list append operation (`++`) and "lift" it onto the relation: if `xss1 <-> xss2` and `yss1 <-> yss2`, then `(xss1 ++ xss2) <-> (yss1 ++ yss2)`. We'll say that the relationship `xss <-> yss` is "reducible" if we can "rewrite" it in that append form. Specifically, it's reducible if there is some `xss1`, `xss2`, `yss1`, `yss2` with `xss1 ++ xss2 = xss` and `yss1 ++ yss2 = yss` where `(xss1 ++ xss2) <-> (yss1 ++ yss2)`. For instance, the example I gave originally is *not* reducible. But, on the other hand, `[[1, 2], [3, 4, 5], [10], [100]] <-> [[1], [2, 3], [], [4, 5], [10, 100]]` is reducible. **Now the question**: What's a "nice" way to characterize the irreducible relationships (ones which cannot be split like that)? Like some sort of criteria for that. The definition itself doesn't work so well for that because it doesn't, on its own, give us a way to see whether or not something is reducible.
English
7
1
11
2K
gelisam ⏸️
gelisam ⏸️@haskell_cat·
@roboguy20 Small typo, should be "then `(xss1 ++ yss1) <-> (xss2 ++ yss2)`". It is true that we can replace a lilist with a lilist containing the same elements, but it is not true that concatenating a list with itself makes it equivalent to any other self-contatenation 😅
English
1
0
2
61
David Young
David Young@roboguy20·
This problem shouldn't run into that decidability issue. We should actually be able to determine reducibility without needing to compare items for equality. Not too much more context to be had. In the presentation of it I saw originally, it was given in categorical terms. That's pretty interesting, although I wanted to give a broader description here. The original description does have a cleaner formulation of irreducibility which I unfortunately kind of forgot (since I ended up thinking about it with reducibility being the primary concept, and irreducibility derived from it)... I believe it's equivalent though (although that would be interesting to show).
David Young tweet media
English
0
0
0
36
Jules
Jules@analytichegel·
@roboguy20 We could think of lilists as lists of X + 1 (where 1 signals the end of a list and the beginning of a new one). If two of these share a 1 at the same place (and they are the same with the 1s removed) the things are reducible.
English
2
0
4
339
David Young
David Young@roboguy20·
That looks pretty close. It sort of reminds me of a weak bisimulation, where the `[] ::` steps are silent steps. I have something pretty similar, but I split it across two inductive relations. One of them represents the situation `flatten yss = xs` by telling you which "block" in yss that each item of xs goes into. My hope there is that this descriptive representation is easier to prove things about than the equality. It was heavily inspired by McBride's work on thinnings. For the second relation: If both sides have a "shift" into a new block at the same position, then this is a place we can split. When we get a split-here, the `f` is the remaining suffix and both parts have the same `f`. As long as what remains of both sides is not simultaneously empty, we accept. I still need to finish the correctness proofs though
David Young tweet mediaDavid Young tweet mediaDavid Young tweet media
English
1
0
3
236
effectfully
effectfully@effectfully·
@roboguy20 Is that it? If it is, I'm gonna be pissed it was so easy. I thought maybe using some clever alteration between the two lists with a non-regular data type or something, but this blunt version appears direct enough. I could perhaps factor out the repetition, but who cares.
effectfully tweet media
English
2
0
7
734
David Young
David Young@roboguy20·
Also: we exclude trivial decompositions that use `[] <-> []`.
English
0
0
2
161
David Young
David Young@roboguy20·
@Aron_Adler Actually, it's not associativity that transitivity corresponds to, it's just the existence of composites.
English
0
0
1
26
David Young
David Young@roboguy20·
You can also look at categories from the somewhat opposite perspective, if you're familiar with preorders. Similar to the monoid case, we can also use this perspective to think about the more general category theory situation. What I just described is monoids as one object categories. But we might think of another somewhat degenerate case: categories with many objects, but between any pair of objects A and B there is at most one morphism A -> B. Similar to how a one object category is exactly the same as a monoid, a category that fits the description I just gave is exactly the same as a preorder: We say that A ≤ B holds in the preorder exactly when there is a morphism A -> B in the category. Then in this situation, we can see the correspondence between the preorder laws and the category laws: - Reflexivity corresponds to the identity law - Transitivity corresponds to the associativity law So, this sort of category with at most one arrow between two objects is like a preorder. Specifically, it's like a preorder where we do not care about the actual *proofs* that A ≤ B holds. As I said, as in the monoid case, we can expand to arbitrary categories to use this perspective to think about what categories are. In the case of the monoid perspective, arbitrary categories are like a kind of "partial monoid." From the preorder perspective, arbitrary categories are like "proof relevant preorders." From that perspective, the various morphisms A -> B in our arbitrary category can be thought to be like proofs of A ≤ B in a "proof relevant preorder."
English
2
1
3
433
aron
aron@Aron_Adler·
starting session 6 of learning lean in 1 hour! last week we defined groups, got deep into what makes a value an identity, and started defining homomorphisms 🌈 today we're going to flesh out homomorphisms further and start proving some theorems about groups 👨‍👩‍👧‍👦
aron@Aron_Adler

Session 5 of Learning Lean™ with Jake will start at 7pm UTC tomorrow 🚨 yesterday we got real deep into a proof that the Nats are associative, one tactic at a time tomorrow we're picking up the pace and will prove that Monoid identity is unique + start defining Groups 🕺

English
2
1
15
1.4K
David Young
David Young@roboguy20·
@TheEduardoRFS I was actually looking at this very recently, because I was curious what some sorta semantic/"non-syntactic" examples of linearity (or something like it) would be like. The category of vector spaces, and the category of relations both give examples of that.
English
2
0
1
37
David Young
David Young@roboguy20·
That's actually more restricted than I expected! That would be restricting it to degree 1 polynomials. Linear types sort of correspond to linear transformations, and the both of those things you mention are multi-linear transformations. (The category of vector spaces on a field do not have the equivalent of contraction and weakening, but they do have the equivalent of exchange. In that context, something like x*x is like "using x twice")
English
2
0
0
43
EduardoRFS.tei
EduardoRFS.tei@TheEduardoRFS·
Ok now, I think diophantine equations are solveable in a purely linear scenario, but is it solveable with grading? The relevance of this is that then I can fully bake common properties, like distributivity, commutativity and associativity. en.wikipedia.org/wiki/Hilbert%2…
English
1
0
13
1.3K
Mitchell Hashimoto
Mitchell Hashimoto@mitchellh·
I had early access to 5.6/Sol for ~month. Sol is my default. It is faster, plans/judges just as good as Fable, and I think produces better overall work. I’ll reach for Fable still for highly targeted debug or performance work with clear reward functions. A cheeky way I describe Sol vs Fable to my friends is that Sol is a charismatic, efficient, talented coworker you’re jealous of. Fable is a genius recluse that is brilliant at its fixations but doesn’t go out, doesn’t date, and you don’t want to hang out with them much lol. Fable is undefeated at highly targeted debug/security/performance goals. It’s a sight to behold and I was never able to get Sol to push as hard in this category. I’ll keep using it for this. Sol is better or comparable at everything else, in my experience. Give it a shot, it’s hard to describe but it’s just more enjoyable to work with. (Disclaimer I have no financial ties to either lab, wasn’t paid for any of this.)
English
229
302
6.8K
1.2M
David Young
David Young@roboguy20·
This doesn't really seem that problematic to me... It seems fairly expected that you'd have *some* kind of surface language desugaring/elaboration, regardless. Wouldn't this be sorta like insisting that we write `forall (A : Type), El(A) -> El(A)` inside our language instead of `forall (A : Type), A -> A` because we're formalizing using a Tarski-style universe?
English
1
0
0
48
o1lo01ol1o
o1lo01ol1o@o1lo01ol1o·
This is maybe merely conventional wrt to the syntax, but Type : Type reads as “Type is a self-classifying universe” and then the Russel style inconsistency in a Cartesian system like MLTT is evident. It’s a “smell” in that the syntax is not functorial in the semantics. Type :^1 Type or some other difference would be better.
English
1
0
1
166
Taelin
Taelin@VictorTaelin·
Fable 5 proved Bend2's consistency in Lean! Bend2 is an unorthodox proof assistant, because it includes "dangerous" features, like unrestricted recursive types, and type-in-type. These features are very useful for programming, as they allow one to write fast interpreters, cleaner abstractions, and more. But they're also infamous for causing paradoxes that would break a proof assistant like Lean, and once broke mathematics itself: - Curry's paradox: "If this sentence is true, then anything follows." - Russell's paradox: "The set of all sets contains itself." The mere presence of these features allow paradoxical statements to be proven, rendering the proof assistant untrustworthy. That's why Lean, Rocq, Agda, Idris and others ban these features, and most type theorists believe they're *inherently* inconsistent. As I've always pointed out, this is not true, and, today, this fact is mathematically checked in Lean. "Wait, Type:Type isn't inconsistent? Why?" Because consistency isn't an isolated property of a feature, it stems from the interaction between different features. Something can be dangerous in a system, yet harmless in other. Bend admits type-in-type and recursive-types by paying a different price: runtime closures cannot be duplicated. This is precisely what allows Bend to be nearly as fast as C, and as parallel as CUDA. But, as a nice bonus, it also allows it to have these features, without breaking consistency. When I talk about this, I'm faced with lots of skepticism from type theorists, and no amount of explaining makes them concede. So, I took the time to formalize Bend in Lean, write down the key properties I wanted to prove (subject reduction, normalization, consistency), plus an informal English argument. I then passed this file to Fable agents which, nearly a day (and several dollars) later, completed a sorry-free Lean file that passes the checker and validates my claims. This doesn't mean the job is done. For example, if Lean itself is inconsistent, then this proof is moot. More likely, there could be a typo on my formalization, or a mismatch between it and the actual implementation. That said, this is a massive step forward, and a strong validation. Next time someone asks, rather than answering with long explanations or "trust me bro", I can ask them to read a tiny Lean file. (The spec itself is tiny, Fable's proof is massive, but one only needs to read the spec, as Lean verified the proof.)
Taelin tweet media
Taelin@VictorTaelin

*sighs* it is already depressing enough that most of you can't understand my posts, but not being able to distinguish them from some technically illiterate SF CEO who thinks they'd proven quantum physics or some shit is another level of stupid problem is, when I write too technically, it tends to just flop, which is why I have to resort to these "AI good!" and "AI bad!" posts that, I admit, may sound a bit over-excited sometimes. that said, the proof is simple enough to be explainable in a way you all can appreciate, so, I'll give it a shot. with you, in its full glory, how Fable contributed to Bend's consistency proof, why it was incredible and, yes, very valid first: consistency is basically a word that means: "can we trust this language to formalize mathematics?". or, equivalently, can someone prove a false statement in it? imagine if someone found a proof of 2+2 = 5 in Lean. that person would be able to use this falsehood to perform arbitrary type-level rewrites, and, thus, prove any theorem (like riemann's hypothesis!) trivially, in a few lines of code. that wouldn't net them $1 million, but it would make for a legendary issue on Lean's GitHub, immediately invalidating any proof checked by Lean and undermining the language's credibility. I obviously don't want that to happen to Bend2 fortunately, the techniques for constructing a consistent proof system are well known, even though details vary case by case. it usually involves two main parts: first, prove it is sound (i.e., that evaluating an expression can't change this type). honestly, that's just the "show us your implementation is not hopelessly buggy". it is the easy part. the second part is much more difficult: "prove every well typed program in your language terminates" this is necessary because infinite loops allow one to encode "paradoxes" (like "this sentence is false") and, to explain it in a very silly way, these paradoxes "confuse" the type checker, and allow you to prove falsehoods. so, if I want people to trust Bend as a proof language, I must be able to convince them there's no way to express an infinite loop in it. programs like "while (true)" must be, somehow, banned by our compiler. but how? the way most proof assistants (like Lean) do it is to 1. not have loops to begin with, 2. ban any kind of non-structural recursion. that means that, to call a function recursively, you must ensure that arguments are getting smaller. that's fairly standard, and fairly easy to do. so, is that it? unfortunately, that's not enough, because, in functional languages, there's another way for infinite loops to manifest: self-replicating λ-terms. for example, consider the following Python program: evil = (lambda f: f(f))(lambda f: f(f)) print evil it hangs forever, even though it has no loops and no recursion. turns out it is very easy to accidentally let some variation of "evil" to creep in, and "evil" allows one to prove falsehoods. for example, if the set of all sets contains itself, you can summon evil via Girard's paradox. and if you allow recursive datatypes to store functions, then, you can summon evil via Curry's paradox: data Evil { bad(f : Evil -> Evil) } // this would break Lean! that problem is not exclusive to proof languages. a similar paradox once caused a crisis in mathematics itself! in 1901, Russel proposed a legendary proof of a false statement in naive set theory, which was THE foundation of mathematics back then. the news was that math itself was broken, and every proof ever written by humanity would to be untrusted. crazy times! of course, this has since been "patched". today, we call it "naive" set theory for a reason! but this shows how hard it is to design a consistent proof system. humanity failed to do so for millenniums! in Rocq, Lean and Agda, the way they avoid these self-replicating λ's is via a series of "patches" - i.e., human engineered antibodies to kill the paradoxes we found in the past. for example, the 'Evil' datatype above is syntactically forbidden by disabling certain shapes of recursive datatypes ("positivity checker"), and Girard's paradox is avoided by having an infinite universe of types ("universe hierarchy"). this disables the "does the set of all sets contain itself" paradox, which, in turn, disables the `evil = λf.f(f) λf.f(f)` summoned by it. this is all solid and stablished, and people are very confident Lean and others are trustworthy. that said - and that's where I tend to change things - I argue that's overkill. while these restrictions indeed avoid paradoxes, they're also very strict, and ban perfectly valid programs. for example, it is impossible to write a fast interpreter (i.e., via HOAS) in these, and alternatives (like PHOAS) are very contrived. this makes these languages substantially less practical. Bend aims to be a proof language that is also viable as a real world programming language, so, it is of my interest to find more permissive termination argument. and that's what I was working on, with the help of Fable my argument goes like this: first, only allow recursion when arguments decrease. so far, this is the same approach used by Lean and others, nothing new here. now, we must find a way to avoid self-replicating λ-terms (like `λf.f(f) λf.f(f)`) from creeping in. that's where we detour. instead of positivity checker and universe hierarchies, I simply re-use a feature of Quantitative Type Theory (QTT) - which, in short, is an industry standard way to have O(1) arrays in an FP lang, and which Bend *already implements* - to forbid non-linear lambdas. In other words, in Bend, lambdas must be used linearly, and, thus, cannot be cloned, and that's enforced by the already existing QTT system. this simple addition is sufficient to prevent all incarnations of `evil = λf.f(f) λf.f(f)` in one strike, cutting the evil in the bud, and ensuring Bend is terminating, as it easily exhausts every known way to introduce non-termination: - infinite loops → there are no loops - infinite recursion → only allow decreasing recursion - self-duplicating λ-terms → lambdas can't be cloned from termination, consistency follows easily. and that's it. this is *obviously* correct and so easy I'm sure even you're confident you can't write infinite loops in Bend. aren't you? now, I must be very clear here. these are all *my* design choices. I didn't ask an AI "pls build a consistent proof language" and then got flattered into thinking I'm a genius. I studied the subject 10 fucking years and used AI to aid me materialize and double check my ideas. this is the antidote I found to AI psychosis. I call it "competency" that said, if the solutions are mine, how Fable helped here? well, the argument per se is obviously sound, and nobody serious would contest it. the problem is that implementing a proof assistant is hard, and it is easy to introduce accidental bugs that detour from the intended semantics. turns out the way that Bend2 wasn't faithful to my intention, for a reason that is legitimately hard to see, and that Fable identified never the less. QTT, as described in the original paper, allowed "relaxing" its checks a bit on certain places of the code. this is important for usability, and harmless to proof languages that use QTT (like Idris2), because they don't rely on QTT for termination. but Bend2 does, and these relaxed checks allowed lambdas to be cloned in some circumstances. Fable read my termination argument, studied the QTT paper, audited the implementation, and found that inconsistency, handing me a proof of Falsehood! full proof below ↓ that was Fable's contribution, and, if you can't see how incredible this is, I don't know what could possibly impress you. as for the solution, Fable proposed a few. all bad. my fix was to split Type in two sorts: one for arbitrary types, and other for lower order values. this lets me have the relaxed checks on positions where lambdas cannot occur, while still ensuring lambdas cannot be cloned and, therefore, self replicate. this is the "elegant proof" I mentioned in the post below!

English
38
44
813
51.5K
o1lo01ol1o
o1lo01ol1o@o1lo01ol1o·
Type : Type is a bad smell though because it it looks like a typing judgement. I get that it can be sound in QTT or other substructural logics but the syntax itself makes me stutter. In Agda, it’s good to be able to have the tower that gives the level of things you can work with; it’s a type hygienic book keeping
English
2
0
1
262