Zulip Chat Archive

Stream: general

Topic: help with proof of equiv of small and big step eval in UTLC


Paige Thomas (Oct 28 2024 at 03:59):

I'm trying to prove that if the reflexive transitive closure of the small step call by name evaluation of an untyped lambda calculus expression holds then the big step evaluation holds; that is, one direction of showing that they are equivalent. I have a proof of the other direction. I was wondering if someone might see a way forward with the proof, or what I might be doing wrong? I haven't yet found a proof in the literature, or at least not one I can follow, or in mathlib. I hope to do the same for call by value, normal order, and applicative order.

import Mathlib.Logic.Relation


@[reducible]
def Symbol_ : Type := String
deriving Inhabited, DecidableEq, Repr


inductive Expr_ : Type
| Var : Symbol_  Expr_
| App : Expr_  Expr_  Expr_
| Lam : Symbol_  Expr_  Expr_
deriving Inhabited, DecidableEq, Repr


def is_lam_expr
  (M : Expr_) :
  Prop :=
   (x : Symbol_) (e : Expr_), M = Expr_.Lam x e


inductive is_bn_small_step
  (sub : Symbol_  Expr_  Expr_  Expr_) :
  Expr_  Expr_  Prop
| rule_1
  (e1 e1' e2 : Expr_) :
  is_bn_small_step sub e1 e1' 
  is_bn_small_step sub (Expr_.App e1 e2) (Expr_.App e1' e2)

| rule_2
  (x : Symbol_)
  (e1 e2 : Expr_) :
  is_bn_small_step sub (Expr_.App (Expr_.Lam x e1) e2) (sub x e2 e1)


inductive is_bn_big_step
  (sub : Symbol_  Expr_  Expr_  Expr_) :
  Expr_  Expr_  Prop
| rule_1
  (x : Symbol_) :
  is_bn_big_step sub (Expr_.Var x) (Expr_.Var x)

| rule_2
  (x : Symbol_)
  (e : Expr_) :
  is_bn_big_step sub (Expr_.Lam x e) (Expr_.Lam x e)

| rule_3
  (x : Symbol_)
  (e e' e1 e2 : Expr_) :
  is_bn_big_step sub e1 (Expr_.Lam x e) 
  is_bn_big_step sub (sub x e2 e) e' 
  is_bn_big_step sub (Expr_.App e1 e2) e'

| rule_4
  (e1 e1' e2 : Expr_) :
  ¬ is_lam_expr e1' 
  is_bn_big_step sub e1 e1' 
  is_bn_big_step sub (Expr_.App e1 e2) (Expr_.App e1' e2)


example
  (sub : Symbol_  Expr_  Expr_  Expr_)
  (M N : Expr_)
  (h1 : Relation.ReflTransGen (is_bn_small_step sub) M N) :
  is_bn_big_step sub M N :=
  by
    induction h1
    case refl =>
      induction M
      case Var x =>
        apply is_bn_big_step.rule_1
      case App e1 e2 ih_1 ih_2 =>
        induction e1 generalizing e2
        case Var e1_x =>
          apply is_bn_big_step.rule_4
          · unfold is_lam_expr
            simp
          · apply is_bn_big_step.rule_1
        case App e1_e1 e1_e2 e1_ih_1 e1_ih_2 =>
          apply is_bn_big_step.rule_4
          · unfold is_lam_expr
            simp
          · exact ih_1
        case Lam e1_x e1_e e1_ih_1 =>
          apply is_bn_big_step.rule_3
          · exact ih_1
          · sorry
      case Lam x e ih_1 =>
        apply is_bn_big_step.rule_2
    case tail N'' N' ih_1 ih_2 ih_3 =>
      induction M generalizing N'' N'
      case Var x =>
        cases ih_3
        cases ih_2
      case App e1 e2 ih_4 ih_5 =>
        cases N''
        case Var N''_x =>
          cases ih_2
        case App N''_e1 N''_e2 =>
          sorry
        case Lam N''_x N''_e =>
          cases ih_2
      case Lam x e ih_4 =>
        cases ih_3
        cases ih_2

Paige Thomas (Oct 28 2024 at 05:49):

Maybe this doesn't hold, or is missing some condition?

Bhavik Mehta (Oct 28 2024 at 11:16):

I don't think this holds, even in the reflexive case; App (Lam "x" (Var "x")) (Var "y") doesn't big-step reduce to itself. It should even be true that big-step reduction can't produce an unreduced applied lambda expression

Paige Thomas (Oct 28 2024 at 19:01):

Did I translate the big step reduction definition wrong? I took it from 6.1 here: https://www.itu.dk/~sestoft/papers/mfps2001-sestoft.pdf

Shreyas Srinivas (Oct 28 2024 at 20:20):

Small step reductions refine big step reductions, not the other way around. This means if you have a big step reduction, then there is a corresponding small step reduction with the same start and end points

Shreyas Srinivas (Oct 28 2024 at 20:21):

Small step reductions allow terms to reduce in non-terminating ways

Shreyas Srinivas (Oct 28 2024 at 20:23):

You can define big step reductions to avoid all that, though I don't see that happening in your setup

Shreyas Srinivas (Oct 28 2024 at 20:26):

I think what you are missing is a Value or NormalForm type

Shreyas Srinivas (Oct 28 2024 at 20:43):

In this case a predicate would suffice

Paige Thomas (Oct 29 2024 at 01:53):

I'm sorry, I'm not sure I entirely follow. Are we saying that this definition of big step reduction is wrong, or just the statement of the theorem, or both?

Paige Thomas (Oct 29 2024 at 01:53):

Shreyas Srinivas said:

In this case a predicate would suffice

Like one of these?

mutual
def is_normal_form :
  Expr_  Prop
  | Expr_.Var _ => True
  | Expr_.App a n => is_neutral_normal_form a  is_normal_form n
  | Expr_.Lam _ n => is_normal_form n

def is_neutral_normal_form :
  Expr_  Prop
  | Expr_.Var _ => True
  | Expr_.App a n => is_neutral_normal_form a  is_normal_form n
  | _ => False
end


mutual
def is_weak_normal_form :
  Expr_  Prop
  | Expr_.Var _ => True
  | Expr_.App a n => is_neutral_weak_normal_form a  is_weak_normal_form n
  | Expr_.Lam _ _ => True

def is_neutral_weak_normal_form :
  Expr_  Prop
  | Expr_.Var _ => True
  | Expr_.App a n => is_neutral_weak_normal_form a  is_weak_normal_form n
  | _ => False
end


mutual
def is_head_normal_form :
  Expr_  Prop
  | Expr_.Var _ => True
  | Expr_.App a _ => is_neutral_head_normal_form a
  | Expr_.Lam _ n => is_head_normal_form n

def is_neutral_head_normal_form :
  Expr_  Prop
  | Expr_.Var _ => True
  | Expr_.App a _ => is_neutral_head_normal_form a
  | _ => False
end


mutual
def is_weak_head_normal_form :
  Expr_  Prop
  | Expr_.Var _ => True
  | Expr_.App a _ => is_neutral_weak_head_normal_form a
  | Expr_.Lam _ _ => True

def is_neutral_weak_head_normal_form :
  Expr_  Prop
  | Expr_.Var _ => True
  | Expr_.App a _ => is_neutral_weak_head_normal_form a
  | _ => False
end

I'm not sure if these are defined correctly either.

Paige Thomas (Oct 29 2024 at 04:01):

I have proofs of

example
  (sub : Symbol_  Expr_  Expr_  Expr_)
  (M N : Expr_)
  (h1 : is_bn_big_step sub M N) :
  is_weak_head_normal_form N :=

and

example
  (sub : Symbol_  Expr_  Expr_  Expr_)
  (M N : Expr_)
  (h1 : is_bn_big_step sub M N) :
  Relation.ReflTransGen (is_bn_small_step sub) M N :=

Shreyas Srinivas (Oct 29 2024 at 09:10):

I will respond in a few hours, but basically it doesn't have to be that complicated. The value language or normal form language needs to be merely a subtype of the expression language

Shreyas Srinivas (Oct 29 2024 at 10:16):

About the definition of big step semantics. Definitions are the writer's choice, so I wouldn't call them wrong. They're just inconvenient in this case. Having said that, big step semantics is usually taught as a way to side-step all the unwanted challenges of handling reduction rules such as getting into nonterminating reduction sequences. So it usually takes the form exprvalue expr \downarrow value

Shreyas Srinivas (Oct 29 2024 at 10:17):

Of course in a formalization valuevalue is often an exprexpr constructed in a certain way. But the valueness of the reduction target is enforced by a predicate

Paige Thomas (Oct 29 2024 at 18:24):

I'm trying to learn the lambda calculus by formalizing it as I go, and I was trying to prove the theorem above as a way to check that I had the definitions right. Maybe the question I should ask is, what would the standard definitions for small step and big step semantics for call by name, call by value, applicative order, and normal order look like in Lean, for instance if they were in Mathlib, and the definition of lambda terms used strings for the type of variables as above.

Paige Thomas (Oct 29 2024 at 18:25):

(instead of say de Bruijn indexes, or locally nameless, or something else)

Paige Thomas (Oct 29 2024 at 18:30):

I'm kind of surprised actually that I don't see the lambda calculus in Mathlib, maybe I should try to make a PR with what I have for definitions, and get feedback on them there?

Paige Thomas (Oct 29 2024 at 18:55):

Or has someone already started on this?

Yaël Dillies (Oct 29 2024 at 18:56):

@Sky Wilshaw has had ideas in this direction

Paige Thomas (Oct 29 2024 at 19:03):

Kayla Thomas said:

I'm kind of surprised actually that I don't see the lambda calculus in Mathlib, maybe I should try to make a PR with what I have for definitions, and get feedback on them there?

I'm sorry, I didn't mean that in any kind of disparaging way.

Yaël Dillies (Oct 29 2024 at 19:17):

Nono, it did not feel disparaging :smile:

Kyle Miller (Oct 29 2024 at 19:30):

I've heard that one design problem is that there are lots of variations on lambda calculus, so which one would be "the" calculus? Or, can it be designed to be extensible to support most (all?) variations?

Paige Thomas (Oct 29 2024 at 19:33):

I'm just learning it, so I'm not sure. If not, maybe different namespaces/files/folders for each variation, and proofs showing equivalence where possible?

Yaël Dillies (Oct 29 2024 at 19:33):

That's precisely where Sky's ideas help :smiling_face: No lamda calculus can supersede the other (eg the recursors they want to generate are incompatible), so one has to do several proofs of their various properties. However, one can parametrise lambda calculi by the various constructs they accept as primitives and then prove a lot of theorems for all lambda calculi simultaneously by pushing the case splits inside the proofs.

Sky Wilshaw (Oct 29 2024 at 19:38):

If I remember correctly, the reason why I didn't go ahead with writing a full lambda calculus and PRing to mathlib was because there was no obvious "right" or "most general" formulation - even wrt (e.g.) names of bound variables.

Sky Wilshaw (Oct 29 2024 at 19:40):

But anyway, here's my general idea: you make a type that parametrises which features you want in your lambda calculus, and then you make your type of terms inductively:

structure LambdaCalculus where
  hasVar : Bool
  hasLambda : Bool
  hasPair : Bool

inductive LambdaTerm (C : LambdaCalculus)
  | var : C.hasVar    LambdaTerm C
  | lam : C.hasLambda  LambdaTerm C  LambdaTerm C
  | pair : C.hasPair  LambdaTerm C  LambdaTerm C  LambdaTerm C
  | left : C.hasPair  LambdaTerm C  LambdaTerm C
  | right : C.hasPair  LambdaTerm C  LambdaTerm C
  | ...

Sky Wilshaw (Oct 29 2024 at 19:41):

Kyle Miller said:

Or, can it be designed to be extensible to support most (all?) variations?

As I understand it, the general difficulty is unifying the different perspectives on naming. I wonder if an implementation in terms of nominal sets could subsume both approaches.

Sky Wilshaw (Oct 29 2024 at 19:41):

(The approaches being explicit names vs de Bruijn indices, etc.)

Sky Wilshaw (Oct 29 2024 at 19:45):

Of course, this approach (like the others I've seen) has the other difficulty that it's fundamentally non-extensible. Typeclasses can only help so much when all of your proofs are structural induction. I don't know a good fix.

Paige Thomas (Oct 29 2024 at 19:46):

Is it an issue with things not working in Mathlib, or just redundant work, to have more than one variation?

Sky Wilshaw (Oct 29 2024 at 19:46):

Mostly redundant work as I see it - but mathlib likes having the "most general version" of a thing, and this idea seems incompatible with that viewpoint so far.

Sky Wilshaw (Oct 29 2024 at 19:48):

The true "most general version" would be a typed lambda calculus, where the UTLC is encoded by setting all of the types to be the same.

Sky Wilshaw (Oct 29 2024 at 19:48):

Or even further, just all of the calculus of constructions... The point is that it's not clear where to stop! (At least to me.)

Kyle Miller (Oct 29 2024 at 19:49):

So for untyped lambda calculus, reflexive objects it is! :laughing:

Paige Thomas (Oct 29 2024 at 19:54):

Maybe a standalone project outside of mathlib? I'd like to use it as a learning aid, so I would be aiming for less generalization.

Sky Wilshaw (Oct 29 2024 at 19:54):

Yes, this is very doable as a standalone project.

Shreyas Srinivas (Oct 29 2024 at 20:03):

To answer your original question @Kayla Thomas : here is a good example of how to formalise this: https://gitlab.mpi-sws.org/FP/semantics-2023/-/blob/main/theories/type_systems/stlc/lang.v?ref_type=heads

Shreyas Srinivas (Oct 29 2024 at 20:05):

It is in Coq, and follows a specific call by value convention for evaluation. In the exercises you find call by name as well

Paige Thomas (Oct 29 2024 at 20:06):

Awesome, thank you!

Shreyas Srinivas (Oct 29 2024 at 20:07):

Yaël Dillies said:

That's precisely where Sky's ideas help :smiling_face: No lamda calculus can supersede the other (eg the recursors they want to generate are incompatible), so one has to do several proofs of their various properties. However, one can parametrise lambda calculi by the various constructs they accept as primitives and then prove a lot of theorems for all lambda calculi simultaneously by pushing the case splits inside the proofs.

I had a similar idea for algorithms formalisation. A inductive type for imperative combinators, parametrised by a type of queries.

Shreyas Srinivas (Oct 29 2024 at 20:07):

The queries can be any basic operations you use, from subsets of the plain RAM model to complex query models

Shreyas Srinivas (Oct 29 2024 at 20:08):

The complexity claim would be parametrised by the query type and simply count how many times each query is called.

Shreyas Srinivas (Oct 29 2024 at 20:11):

Then prove a number of meta theorems about complexity on this combinator language

Shreyas Srinivas (Oct 29 2024 at 20:13):

The call by name version is here: https://gitlab.mpi-sws.org/FP/semantics-2023/-/blob/main/theories/type_systems/stlc/exercises01_sol.v?ref_type=heads

Paige Thomas (Oct 29 2024 at 20:15):

Thank you!

Shreyas Srinivas (Oct 29 2024 at 20:26):

Also, I translated these files and proofs to lean last year and was able to get shorter proofs in many cases using tacticals

Paige Thomas (Oct 29 2024 at 20:28):

Nice. Is the translation in a public repo?

Shreyas Srinivas (Oct 29 2024 at 20:29):

No. I wrote them as ports of my assignments and iirc we weren't supposed to make those public

Shreyas Srinivas (Oct 29 2024 at 20:29):

Now ofc the solutions are available

Shreyas Srinivas (Oct 29 2024 at 20:30):

I'd have to dig around. It'll take me less time to recreate one of these files than find it

Paige Thomas (Oct 29 2024 at 20:32):

Oh. Are you saying it is ok to make them public now though?

Shreyas Srinivas (Oct 29 2024 at 20:37):

No I never uploaded the lean versions

Shreyas Srinivas (Oct 29 2024 at 20:37):

It's buried somewhere deep in one of my machines

Paige Thomas (Oct 29 2024 at 20:38):

No problem.

Shreyas Srinivas (Oct 29 2024 at 20:38):

Hopefully not in windows. I haven't updated it in months

Paige Thomas (Oct 29 2024 at 20:38):

:)

Patrick Stevens (Oct 29 2024 at 23:30):

My spidey senses are tingling a bit at this hasPair : Bool idea. Doesn't that make it hard to phrase "here is how I added pairs to my lambda calculus that previously lacked them" - e.g. doesn't "lambda plus var" give you access to an implementation of "pair" through the Church encoding? Or is that considered out of scope?

Shreyas Srinivas (Oct 29 2024 at 23:43):

Church encodings are painful to deal with. Some syntax sugar can make life simple

Shreyas Srinivas (Oct 29 2024 at 23:49):

As I guessed it didn't take too long to convert a reasonable chunk of that Coq code to lean. I skipped contextual semantics although that's one of the best ways to formulate this stuff I have seen. I have set up sorried out theorem statements that compare big-step and small-step semantics

Code clue

Note : I am using a lot of autoImplicits here

Sky Wilshaw (Oct 29 2024 at 23:50):

Patrick Stevens said:

My spidey senses are tingling a bit at this hasPair : Bool idea. Doesn't that make it hard to phrase "here is how I added pairs to my lambda calculus that previously lacked them" - e.g. doesn't "lambda plus var" give you access to an implementation of "pair" through the Church encoding? Or is that considered out of scope?

I only added pairs as an example to show how to add other language features - there are plenty of other things you could add that would maybe be more useful.

Paige Thomas (Oct 30 2024 at 00:20):

Thank you!

Paige Thomas (Oct 30 2024 at 05:57):

Hmm. Now I'm confused as to where the definitions of the big step semantics in my reference come from and how they relate to what is given in this code (if they do).

Shreyas Srinivas (Oct 30 2024 at 13:32):

The reference looks like a more theoretical paper.

Asei Inoue (Nov 13 2024 at 03:59):

this is my attempt to formalize program semantics

https://github.com/Seasawher/ConcreteSemantics.lean


Last updated: May 02 2025 at 03:31 UTC