[HN Gopher] How to Keep Lambda Calculus Simple ___________________________________________________________________ How to Keep Lambda Calculus Simple Author : azhenley Score : 59 points Date : 2023-07-08 15:32 UTC (7 hours ago) (HTM) web link (hirrolot.github.io) (TXT) w3m dump (hirrolot.github.io) | DonaldPShimoda wrote: | I think the author's title is kind of misleading. | | The lambda calculus _is_ simple. It consists of only four | essential things, and with those four things we can compute | anything that is computable. (I don 't claim that the lambda | calculus is simple to understand -- certainly there are people | who have a hard time grasping it. But that's a different issue.) | | What the author is doing is not "the lambda calculus"; it's an | extended lambda calculus for dependent types. That's a _huge_ | step in terms of complexity. Yes, it might be the simplest lambda | calculus for the task (I didn 't check), but that is not at all | "how to keep lambda calculus simple". Just a very odd choice for | a title, in my opinion. | solomonb wrote: | The Author is _not_ implementing dependent types. They | specifically are implementing Simply Typed Lambda Calculus. | They have stripped out the Pi and Sigma types from the original | paper. | dunham wrote: | The original paper also does plain STLC first in section 2, | and then adds dependent types in section 3. (And finally it | adds the naturals in section 4.) | | In the Idris2 github repository, Guillaume Allais goes a step | further and shows a well-named version. There the types of | terms and values are indexed by the list of names in the | environment and the compiler checks that the manipulation of | deBruin levels and indices is correct: | | https://github.com/idris- | lang/Idris2/blob/main/libs/papers/L... | ggm wrote: | The body says: We distinguish two kinds of | variables: Bound variables and Free ones. The first kind | of variables is called De Bruijn indices: each bound | variable is a natural number (starting from zero) that | designates the number of binders between itself and a | corresponding binder. For example, the term \x - > \y -> x | (the "K combinator") is represented as \_ -> \_ -> 1. (If | we were returning y instead of x, we would write 0 | instead.) | | The footnote says: Contrary to a De Bruijn index, | a De Bruijn level is a natural number indicating the | number of binders between the variable's binder and the | term's root. For example, the term \x -> \y -> x is | represented as \_ -> \_ -> 0, where 0 is the De Bruijn | level of x. The De Bruijn level of y would be 1 if we used | it instead of x.- | seanpquig wrote: | Very interesting blog, but also kind of hilarious that a post | about keeping a concept "simple" proceeds with a dump of insanely | long, dense, esoteric code, variable names and jargon. | | I love functional programming and have had some fun diving deep | into some of the underlying category theory concepts at times, | but I feel like 95% of the time trying to introduce these | advanced concepts into a practical, professional codebase will | lead to severe headache. | solomonb wrote: | I don't think your conclusion is especially fair. This is a | blog post about programming language design and type theory not | general usage of functional programming. | | It also doesn't mention any category theory... | [deleted] | cvoss wrote: | The author criticizes some design choices made by the paper's | authors as not being simple enough. However, in fairness to that | paper, it's aiming for dependently typed lambda calculus and, for | reasons of continuity of exposition, is probably making sub- | optimal choices up front that pay off later. The distinction | between inferable and checkable types, for example, doesn't | really pay off until you need to do beta-reduction _in the type | checker_. The author of the present article has the luxury of | stopping early, with just an STLC implementation. | tempodox wrote: | What the hell is `:@:`? Cannot google that, and the GHC docs | don't tell either. | joek1301 wrote: | It defines a new infix data constructor. So if `it` and `ct` | are values of type `ITerm` and `CTerm` respectively, `it :@: | ct` is a value of type `ITerm`. (It could have been written | using a prefix operator like `ITerm`'s other data | constructors.) | tikhonj wrote: | It's a constructor defined with infix syntax. You can make | constructors with custom infix names by starting the name with | the : character. Alternatively, you could call that constructor | App or something and have the same effect: | data ITerm = Ann CTerm Type | Bound Int | | Free Name | App ITerm CTerm deriving | (Show, Eq) | thaliaarchi wrote: | For anyone confused here, the "simple" here comes from the | simply-typed lambda calculus, that it models. This post addresses | several issues with a tutorial paper on a _dependently-typed_ | lambda calculus, which is what all the build-up is for. | | I would have found this blog post helpful when I was implementing | the original tutorial paper, because I got stuck with the higher- | order abstract syntax for lambdas. The original uses lambda | literals from Haskell, which I couldn't figure it out how to | express in Coq or Rust, because of the type recursion and some | other issues. Whereas, I would easily be able to implement the | first-order alternative presented here in either language. | cubefox wrote: | More on "simple": | | > Church's type theory is a formulation of type theory that was | introduced by Alonzo Church in Church 1940. In certain | respects, it is simpler and more general than the type theory | introduced by Bertrand Russell in Russell 1908 and Whitehead & | Russell 1927a. Since properties and relations can be regarded | as functions from entities to truth values, the concept of a | function is taken as primitive in Church's type theory, and the | l-notation which Church introduced in Church 1932 and Church | 1941 is incorporated into the formal language. | | https://plato.stanford.edu/entries/type-theory-church/ | klysm wrote: | For certain definitions of simple. I don't think it's a | particular useful word because you can use it to justify almost | anything. | a1o wrote: | I thought some like t ::= x | (variable) | \x:T,t | (abstraction) | t t | (application) | true | (constant true) | false | (constant false) | if t then t else t | (conditional) | | Was simpler. | [deleted] | PartiallyTyped wrote: | If you define true and false as abstractions: | true ::= \x\y,x false ::= \x\y,y | | Then the conditional comes for free, since it is an application | of true. You can define not as not ::= \b, b | false true | | You can then define 'and' as and ::= \x\y, x | y false | cvoss wrote: | That won't work in simply typed lambda calculus. (Try to | write down the types involved!) Either untyped or dependently | typed is required. | PartiallyTyped wrote: | I have to admit that I am out of my field, so help me a | bit, you expressed that it must be dependently typed | because false and true are a -> b -> a|b under union (and | a->b->b and a->b->a respectively) so the the abstractions | can take any functions subtyping a->b->a|b, thus to make | them correct you need dependent types to verify that the | abstractions take only True and False? | | Is this correct? | cvoss wrote: | The only types available to you in this flavor of STLC | are bool, bool -> bool, bool -> bool -> bool, (bool -> | bool) -> bool, etc. | | Structurally, if `true := \x\y.x`, the simplest attempt | at typing it would be `true : bool -> bool -> bool`. But | that means `true` can't have type `bool`! | | What you need is to say that true (like false) chooses | one of its two inputs to return, no matter what type that | happens to be. So we introduce a type parameter (think | Java generics, if that's what you're familiar with). | | "Given any type T, `true T` has type T -> T -> T". A | formalized notation for this (there is more than one | notation you'll see) is `true : [?] (T : Type), T -> T -> | T`. And `false` has the same type. This type obviates the | need for a declared `bool` type. | cvoss wrote: | That's a compact data structure, but now show us the type | checker and the evaluator? | | Your data structure is also not the simplest choice of syntax | for lambda calculus, since you require an explicit type | annotation on every function variable. I don't think a user of | your implementation would find it "simple" to write even the | identity function for a non-trivial type. | solomonb wrote: | The typechecker and evaluator for this AST would be super | simple. | | If you want a simple typechecker then you need type | annotations. If you want to have type inference then you | either need to implement unification or go the bidir route | (which still requires top level annotations). You can't have | it both ways. | cvoss wrote: | I'm sure the typechecker is as simple as you suggest for | GP's language; but not showing it at all makes for an | unfair comparison with the article. But the evaluator? | Without out the trick of notation that is [x/y] | substitution? It's not simple! | | You are right that the typechecker gets significantly more | complicated if type annotations are optional. But the | article and the paper are about real implementations of the | language, not arm-chair ones where the burden of type | annotations isn't important. | solomonb wrote: | This article is odd. The author seems to imply they are | introducing NBE in their second redesign, but the original paper | performs NBE as well. ___________________________________________________________________ (page generated 2023-07-08 23:00 UTC)