[HN Gopher] The l-Cube ___________________________________________________________________ The l-Cube Author : todsacerdoti Score : 106 points Date : 2022-01-15 18:25 UTC (4 hours ago) (HTM) web link (en.wikiversity.org) (TXT) w3m dump (en.wikiversity.org) | dataangel wrote: | Can anyone ELI5 the difference between dependent types and what | Rust calls const generics and what C++ calls non-type template | parameters? If I have a type that is templates/generic on an | integer value, e.g. 'MyType<3>' is that a dependent type? | drdeca wrote: | If that 3 can be decided at runtime rather than at compile | time, yes. | | Like, if I can have a type Array<Type,Length> and have a | function f which has as its input type Int, with the name of | the input variable being n, and as its return type | Array<Bool,n> and when you give the return value to something | else, it checks (at compile time) that the length of the array | (i.e. , n ) matches what the other thing is expecting, | | that would be using dependent types. | steveklabnik wrote: | They're subsets of true dependent types, in my understanding. | This is because they can only do this at compile time, not | runtime. There's no way to say, get a number over stdin, put it | in a variable n, and then instantiate MyType<n> with that | variable. You can with true dependent types. | codebje wrote: | A dependent type is a function from a value to a type - where a | value is a runtime value, not a compile time constant. | | Can you do 'MyType<n> foo(int n) {...}' ? | smitty1e wrote: | Is there a symbology primer for the lay reader who finds the Type | Theory table at the bottom a trifle dense? | [deleted] | mdm12 wrote: | You, my friend, are diving into the realm of formal semantics | for programming languages. Benjamin Pierce's Types and | Programming Languages is an excellent reference, but it is not | for the faint of heart. | abeppu wrote: | Maybe a twist on the "why is this useful" question ... | | Users of lisp often are enthusiastic about the idea that lisp is | at its core a minimal wrapper around the untyped lambda calculus | (e.g. plus naming, primitive values and operations, etc). | | Why do we not generally see languages which are minimal wrappers | around these typed lambda calculi? Perhaps intermediate languages | are built closely around these formal systems (e.g. I'm aware | that Haskell Core is closely based around System FC), but | languages which are intended for human authors and readers seem | to introduce a lot of extra machinery. Why is that? | | One guess is that lisps cope with being minimal through use of | macros and metaprogramming, it's difficult for a typed language | to support that level of metaprogramming while maintaining the | various guarantees that one wants from such a system. | Zababa wrote: | > Why do we not generally see languages which are minimal | wrappers around these typed lambda calculi? | | If you add parametric polymorphism, you're not far from ML, | which lives on these days as Standard ML and somewhat through | OCaml. | vilhelm_s wrote: | Cut out and assemble yourself: | https://www.irisa.fr/lande/ridoux/LPAZ/node47.html | azdavis wrote: | I actually wrote a bit about this on my website: | https://azdavis.net/posts/lambda-cube/ | | I've been using my website as a way to practice writing, so | please let me know what you think. | AlexSW wrote: | Nothing to add to the other comments, just wanted to add | another data point that the website (on mobile) was perfectly | clear and the information was all laid out and explained very | well; I have no (constructive) criticism to give. | azdavis wrote: | Thank you! | wlib wrote: | Very nice; you get to the practical lesson of it in a fairly | straightforward manner while keeping interesting tangents where | they aren't distracting. | azdavis wrote: | I really appreciate your feedback, thank you! | | > interesting tangents | | Yeah, I've found that explaining things by first giving an | example and then generalizing often works better than trying | to start with raw theory. | kinow wrote: | Great post! I was going to share this one on the | r/functionalprogramming subreddit when I found yours. A lot | easier to read, and helps reading the Wikiversity afterwards. | Thanks! | azdavis wrote: | Wow thanks! Never had my stuff shared out before to my | knowledge but I appreciate it! I'll take a look :D | plafl wrote: | I actually understood your article about the lambda cube much | better. Also, very nice formatting and clean design, very | readable on my phone. | azdavis wrote: | Thank you! I've tried to make sure that my site reads well | and loads fast, even on mobile. | | There's actually no JS on my site at load time, I run it all | as a build step that transforms Markdown with inline LaTeX | into pure HTML and CSS. | https://github.com/azdavis/azdavis.net | ogogmad wrote: | The syntax part seems to be missing most of the type constructors | of Martin Lof Type Theory, with no apparent ways of expressing | them: - Dependent sum - Cartesian product | - Disjoint union | | [edit] | | More here: | https://en.wikipedia.org/wiki/Calculus_of_constructions | | Turns out they can be expressed after all. | remexre wrote: | Church constructions? | | - Dependent sum Sigma A (\x -> B) = (C : *) | -> ((x : A) -> B x -> C) -> C | | - Cartesian product Pair A B = (i : Bool) -> | (if i then B else A) | | - Disjoint union Sum A B = (C : *) -> (A -> B | -> C) -> C | | EDIT: Ah, started writing before you edited; yeah, (with an | expressive enough type system) lambda's all you need | atennapel wrote: | Your `Sum` is actually a `Pair` as well :). Should be: ``` | Sum A B = (C : *) -> (A -> C) -> (B -> C) -> C ``` | | Note that in the calculus of constructions you can do these | Church encodings of datatypes but you cannot derive an | induction principle for these (only non-dependent folds). For | example you cannot write a `snd` for the `Sigma` above. | | As you allude to, you need a more expressive type system to | derive induction principles (for example "Self types") for | lambda encodings. | neilkakkar wrote: | Asking because I see something that's a lot of mappings with zero | context: Why/ for what is the Lambda-Cube useful? | viewfromafar wrote: | Type theories are formal logic languages. People use formal | logic for all sorts of things but mathematics has the longest | history. This is where people first defined formal languages | and assigned meaning (mathematical meaning.) | | Starting from simply typed lambda calculus ("simple type | theory", also "higher order logic"), one can make various | extensions that make the logic language more expressive. The | lambda cube is a way to systematically organize the space of | type theories. | | Interestingly, the theory has become very relevant for type | systems of normal programming languages. Milner invented ML as | "meta language" for helping with logic and theorem proving, but | it was used widely and today everyone expects generics to work | more or less as in System F polymorphic lambda calculus. | JadeNB wrote: | I think it's useful mostly as a sort of Linnaean classification | device--it's a lot easier to compare different type systems by | placing them on a 'standard' cube, than by choosing ad hoc | points of comparison. | viewfromafar wrote: | For anyone who wants to dive deeper, this a good series of short | video lectures pulled together by students of TU Berlin that | introduces this topic: | https://youtube.com/playlist?list=PLNwzBl6BGLwOKBFVbvp-GFjAA... | wlib wrote: | Somewhat unrelated to this link, but I really think that it's | interesting how much effort we put into the theory side of | languages/types - with almost no one in industry applying these | lessons. The essence of this is just something along the lines of | "What if we unified values with types, so we could have more | useful type checking?" We have billions of dollars being spent | and thousands of smart people working for decades, who don't even | get to use a proper type system - forget about statically-checked | polymorphism or dependent typing. It's not even as if we lost the | ball, we're running in the wrong direction. It's almost trivial | to bolt on gradual typing to a language. It's also not too hard | to retrofit dependent types (just don't bother with Turing- | incompleteness). The whole point of these type systems is to | better model systems, so why not go the extensible route of | letting anyone hook into the "type checking" phase? Import affine | types (that decades old thing that got rust popular); write your | own domain-specific checker, even. It's so frustrating to see how | beautiful ideas just die because we feel comfortable the way | things are now. Maybe language was the wrong abstraction to begin | with. Sorry to derail, but this feels like the only path to truly | popularize the lambda cube as a concept. | [deleted] | ogogmad wrote: | Type theory has uses outside programming. It can be used to | make constructive logic rigorous, which was in fact Martin | Lof's original use for it. | wlib wrote: | I'm aware of math being entirely useful outside of | programming - my little rant is more about how little | programming is inspired by math | exdsq wrote: | I've used Haskell for 18 months in a professional setting. | I prefer C++. Grass is always greener - having endless | abstractions to be mathsy becomes a real pain when you're | trying to build real products. | black_knight wrote: | While making constructive mathematics rigorous was Martin- | Lofs original goal, he was quite early aware of the | applications to computer science. See for instance his | 1979ish paper <<Constructive Mathematics and Computer | Programming>>[0] | | [0]: 1982 version: | https://raw.githubusercontent.com/michaelt/martin- | lof/master... | adamnemecek wrote: | As with all research, the good ideas eventually make it to | production. | dvt wrote: | You said a lot of stuff, and I disagree with most of it, but | I'm open to dialogue. | | > with almost no one in industry applying these lessons | | Straight-up incorrect. Language designers think long and hard | about how typings work in their languages. Rob Pike, Ken | Thompson, and Russ Cox deliberated generics for like a | decade[1] before finally allowing them in Go. | | > a proper type system | | I'm not sure exactly what you mean by "proper" -- but "rigid" | type systems are extremely cumbersome to use practically. | (Typed) l-calculus is an academic example; Haskell is a real- | world example. | | > not too hard to retrofit dependent types (just don't bother | with Turing-incompleteness) | | Hard disagree. It actually _is_ extremely hard: type resolution | is undecidable, for one. So you need to carefully design type | resolution in a way that the "edges" of types don't (or can't) | break the runtime _too_ badly. | | > why not go the extensible route of letting anyone hook into | the "type checking" phase | | Absolutely terrible idea, for many reasons, but mainly because, | if C/C++ macros are any indication, people will abuse any kind | of compile-time (or pre-processing) trickery you give them | access to. | | > write your own domain-specific checker, even | | I guess I'm in the opposite camp here, I think domain specific | languages are an absolute dumpster fire of abstraction and | 99.9% of the time completely exceed the scope of the problem | they're trying to solve. The purpose of a programming language | is to be applicable to many classes of problems, and DSLs fly | in the face of that pretty common-sense tenet. | | [1] https://research.swtch.com/generic | wlib wrote: | I'm on my phone so I can't really give this the response that | it's owed, but most of what I want to say is that I don't | think we disagree as much as you think. My mention of | languages being the wrong approach to general programming is | a key aspect of my position. You're right that DSL's are | usually not a great idea, but that seems like more of an | issue with the "syntax-chasing". The affordances that a | syntax forces upon everyone are why it takes a decade to add | generics to Go and why macros are even used to begin with. As | we see from the various notations used for equivalent math, | we probably we be better off encoding the structure as a | flexible semantics graph and then "projecting the syntax" | however the programmer wants. Keep in mind that what I write | is about possibilities more than what you would be forced to | do every project. You wouldn't expect everyone to import | random type systems more than you would expect everyone of | write and import their own random crypto system libraries. | Some of the most popular languages out there have absolutely | zero static checks. Would it be so bad to write a Turing | complete static check function? Why do we care so much about | decidability that we can't even consider it in useful | exceptions? Also, do you think macros a la lisp would be so | bad if they had more powerful type systems backing them up | and more flexible editors to understand them? | dvt wrote: | > Would it be so bad to write a Turing complete static | check function? | | I think most type systems are, in fact, Turing complete | (there's a fun proof somewhere that TypeScript is, for | example). And most typed languages (Go, C++, Java, | TypeScript) have pretty mature static type-checkers, unless | I'm misunderstanding your ask. | foobiekr wrote: | Oh my goodness do I ever agree about any kind of "use parser | cleverness to make your cute interior domain specific | language" thing as found in Scala and Ruby and a few other | contexts. | | If you want a language, and have legit need, fire up antlr | and build a quick compiler. | | Parser cleverness if the "internal DSl" variety is on par | with the C++ windowing libraries that used heavy operator | overloading to be clever, the stuff like "window += new | Button()" and other nonsense. Just awful and torturous. | rowanG077 wrote: | Go is the poster boy for a language NOT learning anything | from decades of PL theory and research. | ebingdom wrote: | > Straight-up incorrect. Language designers think long and | hard about how typings work in their languages. Rob Pike, Ken | Thompson, and Russ Cox deliberated generics for like a | decade[1] before finally allowing them in Go. | | I think that kind of proves their point. Parametric | polymorphism is one of the most well-understood, least | contentious extensions to the lambda calculus. It's | formalized by System F and has been implemented in | programming languages 40 years ago with great type inference | for an important subset (Hindley-Milner). Yet generics was | highly controversial in the Go community. And now since none | of the standard library was designed with generics in mind, | it's full of unsafe patterns that involve essentially dynamic | typing (e.g., the `Value` method of `Context`). | | Despite Rob Pike et al. designing one of the most popular | languages today (Go), I consider them more experts in systems | rather than programming languages. | | > I'm not sure exactly what you mean by "proper" -- but | "rigid" type systems are extremely cumbersome to use | practically. (Typed) l-calculus is an academic example; | Haskell is a real-world example. | | I find Haskell a joy to use, and I cringe at having to use | languages like Java and Go, which are a minefield of error- | prone programming patterns (like using products instead of | sums to represent "result or error"). Generally speaking, my | Haskell code is shorter, less buggy, and more reusable than | my Go code, so I'm not sure what you mean by "cumbersome". | dvt wrote: | > I find Haskell a joy to use, and I cringe at having to | use languages like Java and Go, which are a minefield of | error-prone programming patterns (like using products | instead of sums to represent "result or error"). Generally | speaking, my Haskell code is shorter, less buggy, and more | reusable than my Go code, so I'm not sure what you mean by | "cumbersome". | | I just want you to realize that you are in the _extreme_ | minority here, but just from a common-sense POV, if I 'm | prototyping a project, looking for product-market fit, the | last thing I want to care about is that my typeability is | recursively enumerable or what-have-you. I just want to | build something quickly without much hoopla (hence the | massive popularity of very weakly-typed languages like JS | and Python). | | FWIW, I think that generics are a mistake in Go, but I was | just trying to counter the point that language designers | don't think long and hard about type-theoretic features. I | think the main issue with generics, wasn't so much an inept | "we don't know how to do this," but rather "is this worth | the code complexity?" and "is this worth the added compile | times?" -- both of which were main selling points of Go. | Zababa wrote: | > I just want you to realize that you are in the extreme | minority here | | Considering how much people love and use Rust, Typescript | and typechecking in Python, I don't think your point | about people prefering JS and Python is that strong. | There is one thing that's sure: people hate typing Car | car = new Car(). var car = new Car()? That's already | better. Python and JS got popular in reaction to | Java/C#/C++, which were painful to use. Even Go now has | type inference, which helps a lot. Now in 2022 it's not | the same as 2005. You can have static types and a fast | time to market. Before thinking about dynamic vs static, | we should first try to see what the best dynamic and | static typing schemes we can come up with. | | > if I'm prototyping a project, looking for product- | market fit, the last thing I want to care about is that | my typeability is recursively enumerable or what-have-you | | That's a bit of a strawman. If I'm prototyping a project, | the last thing I want to care about is the weird API | design that the author of that library I need came up | with, instead of just implementing a common interface. | Python and JS both have iterators for a reason. People in | the functional world talk about monads all the time for a | reason too: it's a general interface that allows you to | write code fast. Nullable code is the same as async code, | is the same as iteration (well, mapping) code, is the | same as parsing code. | youerbt wrote: | > > just from a common-sense POV, if I'm prototyping a | project, looking for product-market fit, the last thing I | want to care about is that my typeability is recursively | enumerable or what-have-you. | | Then don't. It's not like Haskell forces you to, that's a | myth about Haskell. You can get away with strings and | integers everywhere if you so fancy. | | There is just no common-sense in thinking that computer | assisted programming is slower than unassisted. | ebingdom wrote: | > I just want you to realize that you are in the extreme | minority here | | I'm in the minority because I've spent an unusual amount | of time investing in my understanding of programming | languages and their features, not because I have some | fringe unjustified opinion. | | > if I'm prototyping a project, looking for product- | market fit, the last thing I want to care about is that | my typeability is recursively enumerable or what-have- | you. | | With this comment you've lost your credibility in my | mind. No one actually goes through this train of thought. | I don't wonder about recursive enumerability whenever I | start a project. I just use tools that help me build high | quality software, such as principled static type systems. | dvt wrote: | > I'm in the minority because I've spent an unusual | amount of time investing in my understanding of | programming languages and their features, not because I | have some fringe unjustified opinion. | | I was just making a statement of fact. You're probably a | very good programmer (as most _type nerds_ , to coin a | new term, tend to be), but still in the minority. Most | programmers are not very good, and even good ones | sometimes want to build things fast. (Where type | ambiguity or even incorectness is accepted as a viable | trade-off.) | | This is why I stopped prototyping in Java, for example. | JS just let me "do stuff" without thinking about it too | hard. Lower code quality? Of course; but it let me try | out more ideas in the same amount of time. Should you use | JavaScript to write the operating system of a pacemaker? | Probably not. | obviouslynotme wrote: | This is because type checking is the least impactful part | of generics. As you said, that is a solved problem. The | reason Go took so long thinking about generics has to do | with their code generation. There are steep tradeoffs in | compilation vs run time as well as compiler complexity and | language complexity in general. Go's purpose was to be very | fast to compile and run, born in a company where compile | times for several projects run for hours even with massive | parallelization. | | There is very good reason that Go's initial compromise was | to use built-ins for a small number of highly useful | generics like arrays and maps. | tluyben2 wrote: | This is what I have noticed writing a lot of software | communicating with systems (old systems) with not very well | specified input & output; I first want to write drivel and make | it work. I don't want to write/change 1000s of vars/fields | while i'm not sure if I even need them etc. Then I want to | throw away and refactor with lessons learned and I want to keep | doing that, adding types (and sometimes proofs). Maybe with | compiler flags which enforces static typing for the 'production | version'. | shrimpx wrote: | A core problem is that you don't need super fancy type systems | to build robust systems in practice. After a certain threshold, | type theory research is exploring what's possible in theory, | like pure mathematics, not what would be good for | practitioners. | | But one area of large impact for this pure type theory research | seems to be the mechanization of mathematics. It looks probable | that in the future, the standard way to do mathematics will be | by programming it in a proof development system, aka | dependently typed programming language. | ogogmad wrote: | > It looks probable that in the future, the standard way to | do mathematics will be by programming it in a proof | development system, aka dependently typed programming | language. | | This is a controversial opinion, and not all mathematicians | are enthusiastic about it, whether rightly or wrongly. | Michael Harris, for instance, is a major sceptic and | opponent. | | It is still a very interesting idea. And it has had some | notable successes already. | armchairhacker wrote: | Most of the less abstract parts of this research (generic and | polymorphic types w/ variance, ADT structs/unions, even type | functions) are in most modern languages (Scala, TypeScript, | C++20, Kotlin, Swift, Go). Rust even has affine types | | The more abstract parts like dependent types are _really_ | complicated and even unintuitive to use. See: issues with | Rust's borrow checker, or Haskell being so confusing. | | Earlier languages like C and Java are mostly legacy code, they | use libraries in legacy code, or they're for developers | familiar with them. | | Untyped scripting languages are fine for scripts. Honestly idk | why developers write big libraries in untyped languages like | JavaScript and Python. | | The main case where advanced formal methods are particularly | useful is proving program correctness. And AFAIK this stuff | _is_ used by industry, although you don't hear about it as | much. The thing is, most programs either don't "need" to be | proved, or they're way too big. | dwohnitmok wrote: | Dependent types aren't terribly complicated intrinsically, | see e.g. https://shuangrimu.com/posts/language-agnostic- | intro-to-depe... for one explanation of dependent pairs. | | Nor do I think the value of dependent types comes from | proofs, which I agree are still too cumbersome to really use | at a large scale for most codebases. | | My hypothesis for the sweet spot for dependent types is to | express invariants, and then use other mechanisms such as | property tests to check those invariants, rather than actual | proofs (except for trivial proofs). This does mean that I | favor a very proof irrelevant style of dependently typed | programming (i.e. in the language of Agda, way more `prop` | than `set` or in Idris lots of zero multiplicities for | dependently typed arguments) which is quite different from | the way a lot of current dependently-typed code is written. | plafl wrote: | If I understood the article correctly the four type of | functions are present in Julia: julia> | factorial(3) # term -> term 6 julia> typeof(3) # | term -> type Int64 julia> one(Float32) # type -> | term 1.0f0 julia> Vector{Float32} # type -> type | Vector{Float32} (alias for Array{Float32, 1}) | | And actually more mixed: julia> using | StaticArrays julia> SVector{3, Float32}(1, 2, 3) | 3-element SVector{3, Float32} with indices SOneTo(3): | 1.0 2.0 3.0 julia> convert(Float32, 1) | 1.0f0 | | Julia is of course dynamic, so maybe it's cheating? I'm | writing this comment because all the greek in that cube, and | actually using a cube, makes this look like very abstract | stuff but I consider Julia a _very_ practical language. | | > Untyped scripting languages are fine for scripts. Honestly | idk why developers write big libraries in untyped languages | like JavaScript and Python. | | And yet it works | chriswarbo wrote: | > The more abstract parts like dependent types are really | complicated and even unintuitive to use. | | I disagree with this: dependent types are _way_ easier than | lots of the convoluted schemes that non-dependent languages | have come up with. | | As a simple example, dependently-typed languages don't need | parametric polymorphism or generics: we can achieve the same | thing by passing types as arguments; e.g. | map: (inType: Type) -> (outType: Type) -> (f: inType -> | outType) -> (l: List inType) -> List outType map | inType outType f l = match l with Nil inType | -> Nil outType Cons inType x xs -> Cons outType (f | x) (map inType outType f xs) | | When I program without dependent types, I regularly find | myself getting "stuck" inadvertently; knowing that (a) | there's no way to make my current approach work in this | language, (b) that it would be trivial to make it work if I | could pass around types as values, (c) that I need to throw | away what I've done and choose a different solution, and (d) | the alternative solution I'll end up with will be less | correct and less direct than my original approach (e.g. | allowing more invalid states) | andrewflnr wrote: | Is there work on cleanly subsetting dependently typed | languages to ones where well-typed-ness can be | automatically proven? That's not generally the case, right? | I think those concerns are basically why people come up | with less-general typing mechanisms. ___________________________________________________________________ (page generated 2022-01-15 23:00 UTC)