Full reduction and GADTs Didier Rmy (Joint work with Gabriel - - PowerPoint PPT Presentation

full reduction and gadts
SMART_READER_LITE
LIVE PREVIEW

Full reduction and GADTs Didier Rmy (Joint work with Gabriel - - PowerPoint PPT Presentation

Full reduction and GADTs Didier Rmy (Joint work with Gabriel Scherer) Gallium INRIA IFIP WG 2.8, May 2015 Typed programming languages We all love types, for many reasons. . . But, before all, because of type soundness Once programs are


slide-1
SLIDE 1

Full reduction and GADTs

Didier Rémy (Joint work with Gabriel Scherer)

Gallium – INRIA

IFIP WG 2.8, May 2015

slide-2
SLIDE 2

Typed programming languages

We all love types, for many reasons. . . But, before all, because of type soundness Once programs are well-typed, they are always correct...

slide-3
SLIDE 3

Typed programming languages

We all love types, for many reasons. . . But, before all, because of type soundness Once programs are well-typed, they are often correct...

slide-4
SLIDE 4

Typed soundness —Our slogan

“ Well-typed programs do not go wrong ”

slide-5
SLIDE 5

Typed soundness —Our slogan

“ Well-typed programs do not go wrong ” But what does this means?

Closed, well-typed terms never reduce to an error:

∅ ⊢ a : τ = ⇒ ∀b, a

− → b, b / ∈ Errors

slide-6
SLIDE 6

Typed soundness —Our slogan

“ Well-typed programs do not go wrong ” But what does this means?

Closed, well-typed terms never reduce to an error:

∅ ⊢ a : τ = ⇒ ∀b, a

− → b, b / ∈ Errors

The term π1 true is an error, but it is ill-typed. —So we are happy.

slide-7
SLIDE 7

Typed soundness —Our slogan

“ Well-typed programs do not go wrong ” But what does this means?

Closed, well-typed terms never reduce to an error:

∅ ⊢ a : τ = ⇒ ∀b, a

− → b, b / ∈ Errors

The term π1 true is an error, but it is ill-typed. —So we are happy.

  • However. . .

λ(x) (π1 true) is not an error, but it is still ill-typed. —Should we be upset?

Should we fix/improve our type system to accept this?

slide-8
SLIDE 8

Typed soundness —The slogan revisited

λ(x) (π1 true) Type errors are bad even in not-yet-used parts of a program.

slide-9
SLIDE 9

Typed soundness —The slogan revisited

λ(x) (π1 true) Type errors are bad even in not-yet-used parts of a program. λ(x) (λ(y) π1 y) true

full

− → λ(x) (π1 true) Latent type errors are also bad.

Problem: These errors are not ruled out by type soundness for CBV.

slide-10
SLIDE 10

Typed soundness —The slogan revisited

λ(x) (π1 true) Type errors are bad even in not-yet-used parts of a program. λ(x) (λ(y) π1 y) true

full

− → λ(x) (π1 true) Latent type errors are also bad.

Problem: These errors are not ruled out by type soundness for CBV. Solution: Full reduction should be used to test type soundness!

This will evaluate open subterms, even under λ’s.

Revised slogan (with full reduction) “ Well-typed program fragments do not go wrong ”

slide-11
SLIDE 11

Benefit of full reduction

Detects more errors. Hence, as a corollary, type soundness is a stronger result! Makes typechecking more modular: You are not forced to use your functions to see errors in their bodies. Share the meta-theoretical study between CBV and CBN. Also gives a more abstract view of programs Even in languages with a CBV semantics, full reduction may be used to understand programs when efficiency is not a concern. Give a more solid ground Even if a full-fledged language uses CBV, it is reassuring if its core subset is sound and confluent for full reduction.

slide-12
SLIDE 12

Benefit of full reduction

Detects more errors. Hence, as a corollary, type soundness is a stronger result! Makes typechecking more modular: You are not forced to use your functions to see errors in their bodies. Share the meta-theoretical study between CBV and CBN. Also gives a more abstract view of programs Even in languages with a CBV semantics, full reduction may be used to understand programs when efficiency is not a concern. Give a more solid ground Even if a full-fledged language uses CBV, it is reassuring if its core subset is sound and confluent for full reduction.

Beware! We’ve been spoiled by decades during which our languages were sound for full reduction and these properties could be taken for granted.

slide-13
SLIDE 13

Benefit of full reduction

Detects more errors. Hence, as a corollary, type soundness is a stronger result! Makes typechecking more modular: You are not forced to use your functions to see errors in their bodies. Share the meta-theoretical study between CBV and CBN. Also gives a more abstract view of programs Even in languages with a CBV semantics, full reduction may be used to understand programs when efficiency is not a concern. Give a more solid ground Even if a full-fledged language uses CBV, it is reassuring if its core subset is sound and confluent for full reduction.

Beware! We’ve been spoiled by decades during which our languages were sound for full reduction and these properties could be taken for granted. — But they are not true anymore!

slide-14
SLIDE 14

Full reduction with GADTs

type _ tag = | TInt : int tag | TString : string tag let join (type a) (x, y : a) (tag : a tag) : a = match tag with | TInt → x + y | TString → x ^ y

slide-15
SLIDE 15

Full reduction with GADTs

type _ tag = | TInt : int tag | TString : string tag let join (type a) (x, y : a) (tag : a tag) : a = match tag with | TInt → x + y — we assume a = int | TString → x ^ y

slide-16
SLIDE 16

Full reduction with GADTs

type _ tag = | TInt : int tag | TString : string tag let join (type a) (x, y : a) (tag : a tag) : a = match tag with | TInt → x + y | TString → x ^ y — we assume a = string

slide-17
SLIDE 17

Full reduction with GADTs

type _ tag = | TInt : int tag | TString : string tag let join (type a) (x, y : a) (tag : a tag) : a = match tag with | TInt → x + y | TString → x ^ y The term ( join 3 4) has the following normal form: fun tag → match tag with | TInt → 3 + 4 | TString → 3 ^ 4

slide-18
SLIDE 18

Full reduction with GADTs

type _ tag = | TInt : int tag | TString : string tag let join (type a) (x, y : a) (tag : a tag) : a = match tag with | TInt → x + y | TString → x ^ y The term ( join 3 4) has the following normal form: fun tag → match tag with | TInt → 3 + 4 | TString → 3 ^ 4

slide-19
SLIDE 19

What to do with GADTs?

Should we give up full reduction altogether? Consider this variant of join: let join (type a) (x, y : a) (tag : a tag) : a ∗ string = match tag with | TInt → (x + y, "an" ^ "int") | TString → (x ^ y, "a" ^ " string ") The string computation does not depend on the assumptions and could be safely reduced.

Our goal

Find the right constructs to allow full reduction in the presence of GADTs.

slide-20
SLIDE 20

Implicitly-typed System F with pairs

Terms: a, b ::= x | λ(x) a | a a | (a, a) | πi a Evaluation contexts ( for full reduction ) E ::=

| λ(x) E | E a | a E | (E, a) | (a, E) | πi E

Reduction rules: (λ(x) a) b ◦ → a[b/x] πi (a1, a2) ◦ → ai

Context

a ◦ → b E[a] − → E[b] Errors: D ::=

a | πi

Destructor contexts c ::= λ(x) a | (a, b) Constructors E ::=

  • E
  • D[ c ]
  • D[c]
  • Errors
slide-21
SLIDE 21

Type system Implicitly typed

τ, σ ::= α | τ → σ | τ ∗ σ | ∀(α) τ Typing rules Γ, x : τ ⊢ x : τ Γ, x : τ ⊢ a : σ Γ ⊢ λ(x) a : τ → σ Γ ⊢ a : τ → σ Γ ⊢ b : τ Γ ⊢ a b : σ Γ ⊢ a : τ Γ ⊢ b : σ Γ ⊢ (a, b) : τ ∗ σ Γ ⊢ a : τ1 ∗ τ2 Γ ⊢ πi a : τi

Gen

Γ, α ⊢ a : τ Γ ⊢ a : ∀(α) τ

Inst

Γ ⊢ a : ∀(α) τ Γ ⊢ σ Γ ⊢ a : τ[σ/α]

slide-22
SLIDE 22

Soundness holds with full reduction

For all variants of System F (F<:, MLF, . . . )

Type soundness breaks

with inconsistent logical assumptions.

slide-23
SLIDE 23

Adding propositions

P ::= ⊤ | P ∧ P | . . . Logical propositions | τ ≤ τ | . . . Atomic propositions How can we add support for logical assumptions to our system?

slide-24
SLIDE 24

Adding propositions

P ::= ⊤ | P ∧ P | . . . Logical propositions | τ ≤ τ | . . . Atomic propositions How can we add support for logical assumptions to our system? τ ::= . . . | ∀(α | P) τ Γ ⊢ P Γ ⊢ a : τ Γ ⊢ τ ≤ σ Γ ⊢ a : σ . . .

slide-25
SLIDE 25

Adding propositions

P ::= ⊤ | P ∧ P | . . . Logical propositions | τ ≤ τ | . . . Atomic propositions How can we add support for logical assumptions to our system? τ ::= . . . | ∀(α | P) τ Γ ⊢ P Γ ⊢ a : τ Γ ⊢ τ ≤ σ Γ ⊢ a : σ . . . Replacing generalization and instantiation typing rules (the obvious way):

Gen

Γ, α, P ⊢ a : τ Γ ⊢ a : ∀(α | P) τ

Inst

Γ ⊢ a : ∀(α | P) τ Γ ⊢ σ Γ ⊢ P[σ/α] Γ ⊢ a : τ[σ/α]

slide-26
SLIDE 26

Adding propositions

P ::= ⊤ | P ∧ P | . . . Logical propositions | τ ≤ τ | . . . Atomic propositions How can we add support for logical assumptions to our system? τ ::= . . . | ∀(α | P) τ Γ ⊢ P Γ ⊢ a : τ Γ ⊢ τ ≤ σ Γ ⊢ a : σ . . . Replacing generalization and instantiation typing rules (the obvious way):

Gen

Γ, α, P ⊢ a : τ Γ ⊢ a : ∀(α | P) τ

Inst

Γ ⊢ a : ∀(α | P) τ Γ ⊢ σ Γ ⊢ P[σ/α] Γ ⊢ a : τ[σ/α] Subsumes System F, F<:, MLF, can encode GADTs: ∀(α | ⊤) σ ∀(α | α ≤ τ) σ ∀(α | α ≥ τ) σ (σ ≤ τ) ∧ (τ ≤ σ)

slide-27
SLIDE 27

The naive rules are unsound

slide-28
SLIDE 28

The naive rules are unsound

α, (B ≤ B ∗ B) ⊢ true : B α, (B ≤ B ∗ B) ⊢ B ≤ B ∗ B α, (B ≤ B ∗ B) ⊢ true : B ∗ B α, (B ≤ B ∗ B) ⊢ (π1 true) : B ∅ ⊢ (π1 true) : ∀(α | B ≤ B ∗ B) B

slide-29
SLIDE 29

Only consistent abstractions are erasable

An abstraction on (α | P) is consistent when P is satisfied for some type σ substituted for α.

Gen

Γ, α, P ⊢ a : τ Γ ⊢ P[σ/α] Γ ⊢ a : ∀(α | P) τ

slide-30
SLIDE 30

Only consistent abstractions are erasable

An abstraction on (α | P) is consistent when P is satisfied for some type σ substituted for α.

Gen

Γ, α, P ⊢ a : τ Γ ⊢ P[σ/α] Γ ⊢ a : ∀(α | P) τ

If you cannot prove satisfiability (e.g. B ≤ B ∗ B), you cannot use this rule. Previous calculi e.g. F<: can still be expressed, since (α | α ≤ σ) is always consistent (satisfied by α = σ).

slide-31
SLIDE 31

Only consistent abstractions are erasable

An abstraction on (α | P) is consistent when P is satisfied for some type σ substituted for α.

Gen

Γ, α, P ⊢ a : τ Γ ⊢ P[σ/α] Γ ⊢ a : ∀(α | P) τ

If you cannot prove satisfiability (e.g. B ≤ B ∗ B), you cannot use this rule. Previous calculi e.g. F<: can still be expressed, since (α | α ≤ σ) is always consistent (satisfied by α = σ). But GADTs cannot be expressed with consistent abstraction only.

Inconsistent abstraction must delay the evaluation. What is the right design for that?

slide-32
SLIDE 32

Only consistent abstractions are erasable

An abstraction on (α | P) is consistent when P is satisfied for some type σ substituted for α.

Gen

Γ, α, P ⊢ a : τ Γ ⊢ P[σ/α] Γ ⊢ a : ∀(α | P) τ

If you cannot prove satisfiability (e.g. B ≤ B ∗ B), you cannot use this rule. Previous calculi e.g. F<: can still be expressed, since (α | α ≤ σ) is always consistent (satisfied by α = σ). But GADTs cannot be expressed with consistent abstraction only.

Inconsistent abstraction must delay the evaluation. What is the right design for that?

slide-33
SLIDE 33

Explicit vs. Implicit use of hypotheses

In dependently-typed languages, logical propositions are represented as

  • types. Assumptions are introduced using just λ-abstraction λ(z : P) a and

used by explicitly referring to the assumption z: (fun (type a) (x, y : a) (tag : a tag) → match tag with | TInt (z : a = int) → (z x) + (z y) | TString (z : a = string) → (z x) ^ (z y)) If each use of an assumption is marked by a variable, all dangerous redexes are blocked by those variables. The same happens in functional intermediate typed representations (e.g. System FC). But marking all uses of assumptions explicitly is a burden for the programmer: it is too fine grain. Assumptions should be usable implicitly in derivations, just as consistent abstraction, for both convenience and erasability.

slide-34
SLIDE 34

Explicit vs. Implicit use of hypotheses

In dependently-typed languages, logical propositions are represented as

  • types. Assumptions are introduced using just λ-abstraction λ(z : P) a and

used by explicitly referring to the assumption z: fun (tag : int tag) → match tag with | TInt (z : int = int) → (z 3) + (z 4) | TString (z : int = string) → (z 3) ^ (z 4) If each use of an assumption is marked by a variable, all dangerous redexes are blocked by those variables. The same happens in functional intermediate typed representations (e.g. System FC). But marking all uses of assumptions explicitly is a burden for the programmer: it is too fine grain. Assumptions should be usable implicitly in derivations, just as consistent abstraction, for both convenience and erasability.

slide-35
SLIDE 35

Introducing (possibly) inconsistent assumptions a ::= . . . | δ(a, φ.b) | ⋄ τ ::= . . . | [P]

slide-36
SLIDE 36

Introducing (possibly) inconsistent assumptions a ::= . . . | δ(a, φ.b) | ⋄ τ ::= . . . | [P] Γ ⊢ P Γ ⊢ ⋄ : [P] Γ ⊢ a : [P] Γ, φ : P ⊢ b : τ Γ ⊢ δ(a, φ. b ) : τ

slide-37
SLIDE 37

Introducing (possibly) inconsistent assumptions a ::= . . . | δ(a, φ.b) | ⋄ τ ::= . . . | [P] Γ ⊢ P Γ ⊢ ⋄ : [P] Γ ⊢ a : [P] Γ, φ : P ⊢ b : τ Γ ⊢ δ(a, φ. b ) : τ Evaluation E ::= . . . | δ(E, φ.b) | ✘✘✘✘✘

δ(a, φ.E) δ(⋄, φ.b) ◦ → b

slide-38
SLIDE 38

GADTs, sound edition

type ’a tag = | TInt of [’ a = int] | TString of [’ a = string] let join (type a) (x, y : a) (tag : a tag) : a ∗ string = match tag with | TInt z → δ(z, φ. (x + y, "an" ^ "int")) | TString z → δ(z, φ. (x ^ y, "a" ^ " string ")) We may block the whole branch, as done in OCaml or Haskell

slide-39
SLIDE 39

GADTs, sound edition

type ’a tag = | TInt of [’ a = int] | TString of [’ a = string] let join (type a) (x, y : a) (tag : a tag) : a ∗ string = match tag with | TInt z → (δ(z, φ. x + y), "an" ^ "int") | TString z → (δ(z, φ. x ^ y), "a" ^ "string") We may block the whole branch, as done in OCaml or Haskell We also offer more flexibility between implicit and explicit use of assumptions.

slide-40
SLIDE 40

GADTs, sound edition

type ’a tag = | TInt of [’ a = int] | TString of [’ a = string] let join (type a) (x, y : a) (tag : a tag) : a ∗ string = match tag with | TInt z → (δ(z, φ. x) + δ(z, φ. y), "an" ^ "int") | TString z → (δ(z, φ. x) ^ δ(z, φ. y), "a" ^ "string") We may block the whole branch, as done in OCaml or Haskell We also offer more flexibility between implicit and explicit use of assumptions.

slide-41
SLIDE 41

GADTs, sound edition

type ’a tag = | TInt of [’ a = int] | TString of [’ a = string] let join (type a) (x, y : a) (tag : a tag) : a ∗ string = match tag with | TInt z → δ(z, φ. (x + y, "an" ^ "int" )) | TString z → δ(z, φ. (x ^ y, "a" ^ " string " )) We may block the whole branch, as done in OCaml or Haskell We also offer more flexibility between implicit and explicit use of assumptions. Can we do even better? leave the use of the assumption implicit in the whole scope but say explicitly that "an" ^ "int" is not using the assumption?

slide-42
SLIDE 42

Assumption hiding

Assume only F is implicitly using the assumption in: δ

  • a, φ. E
  • F
  • b

Then E and b are unnecessarily blocked.

slide-43
SLIDE 43

Assumption hiding

Assume only F is implicitly using the assumption in: δ

  • a, φ. E
  • F
  • b

Then E and b are unnecessarily blocked. We may write E

  • δ
  • a, φ. F
  • b
slide-44
SLIDE 44

Assumption hiding

Assume only F is implicitly using the assumption in: δ

  • a, φ. E
  • F
  • b

Then E and b are unnecessarily blocked. We may write E

  • δ
  • a, φ. F
  • b

For flexibility, we allow un-blocking a subterm by disabling an assumption. E

  • δ(a, φ. F
  • hide φ in b
  • )
slide-45
SLIDE 45

Assumption hiding

Assume only F is implicitly using the assumption in: δ

  • a, φ. E
  • F
  • b

Then E and b are unnecessarily blocked. We may write E

  • δ
  • a, φ. F
  • b

For flexibility, we allow un-blocking a subterm by disabling an assumption. E

  • δ(a, φ. F
  • hide φ in b
  • )
  • Formally

a ::= . . . | hide φ in b Γ ⊢ ∆ Γ, ∆ ⊢ a : τ Γ , φ : P , ∆ ⊢ hide φ in a : τ

slide-46
SLIDE 46

GADTs, last edition

type ’a tag = | TInt of [’ a = int] | TFloat of [’ a = float ] let join (type a) (x, y : a) (tag : a tag) : a ∗ string = match tag with | TInt z → δ(z, φ. (x + y, hide φ in "an" ^ "int" )) | TString z → δ(z, φ. (x ^ y, hide φ in "a" ^ " string " )) The evaluation of "an" ^ "int" need not be blocked anymore. We offer a continuity between implicit and explicit use of assumptions.

slide-47
SLIDE 47

Mixing full and weak reduction: confluence is broken!

Suppose a − → b. We have a confluence problem: (λ(x) δ(y, φ. E[x] )) a (λ(x) δ(y, φ. E[x] )) b δ(y, φ. E[hide φ in a ] ) δ(y, φ. E

  • hide φ in b
  • )

✟ ✟

− − − → A term in reducible position before substitution, should remain reducible after substitution.

slide-48
SLIDE 48

Mixing full and weak reduction: confluence is broken!

Suppose a − → b. We have a confluence problem: (λ(x) δ(y, φ. E[x] )) a (λ(x) δ(y, φ. E[x] )) b δ(y, φ. E[hide φ in a ] ) δ(y, φ. E

  • hide φ in b
  • )

✟ ✟

− − − → A term in reducible position before substitution, should remain reducible after substitution.

Idea

insert hide φ when substitution traverses the guard φ.

slide-49
SLIDE 49

Mixing full and weak reduction: confluence is restored.

Suppose a − → b. We have a confluence problem: (λ(x) δ(y, φ. E[x] )) a (λ(x) δ(y, φ. E[x] )) b δ(y, φ. E[hide φ in a ] ) δ(y, φ. E

  • hide φ in b
  • )

A term in reducible position before substitution, should remain reducible after substitution.

Idea

insert hide φ when substitution traverses the guard φ.

Result

The system is sound for full-reduction and confluent.

slide-50
SLIDE 50

Mixing full and weak reduction: confluence is restored.

Suppose a − → b. We have a confluence problem: (λ(x) δ(y, φ. E[x] )) a (λ(x) δ(y, φ. E[x] )) b δ(y, φ. E[hide φ in a ] ) δ(y, φ. E

  • hide φ in b
  • )

A term in reducible position before substitution, should remain reducible after substitution.

Idea

insert hide φ when substitution traverses the guard φ.

Result

The system is sound for full-reduction and confluent.

Notice: hide φ in b is useful for flexibility, but required for confluence.

slide-51
SLIDE 51
slide-52
SLIDE 52

Technical details Evaluation contexts

We now need to scan under δ’s for reducible terms under hides. E ::= . . . | δ(a, φ.E) | hide φ in E In δ(a, φ.b), b is guarded by the assumption variable φ, while hide φ in a releases the guard φ. Evaluation contexts are the unguarded ones:

Context

a ◦ → b guard∅ (E) = ∅ E[a] − → E[b]

where guardS (λ(x) E) := guardS (E) guardS () := S guardS (δ(E, φ.b)) := guardS (E) guardS (δ(a, φ.E)) := guard S, φ (E) guardS (hide φ in E) := guard S \ {φ} (E)

slide-53
SLIDE 53

Technical details Substitution

We have changed the notion of substitution to insert hidings: (λ(x) a) b ◦ → a[b/x]∅ x[c/y]S := x y[c/y]S := hide S in c (λ(x) a)[c/y]S := λ(x) (a[c/y]S) δ(a, φ.b)[c/y]S := δ(a[c/y]S, φ.b[c/y] S,φ) (hide φ in a)[c/y]S := hide φ in a[c/y] S\{φ} We have also changed the notion of reduction contexts. These changes are minor as they do not change the term structure, just hiding information, which can be seen as annotations on terms.

slide-54
SLIDE 54

Technical details Related works

Mixing full and weak reduction is a known a problem in the term rewriting community. In λ-calculus, the solution is to extend weak reduction to allow reduction of subterms under abstractions on which the computation does not depend. Our solution is somehow similar, but we first had to introduce explicit (blocking and unblocking) marks for logical dependencies.

slide-55
SLIDE 55

More technical details Soundness proof

  • 1. Eliminating hides

We simulate computation with hiding in the language without hiding (and normal β-reduction) by let-extruding hiding constructs. δ(b, φ. E[hide φ in a ] ) ֒ → let x = abs(E, a) in δ(b, φ. E[app(x, E) ]) If |a| is the ֒ → normal form of a: If a − → b then |a| − →∗ |b|. a ∈ Errors ⇐ ⇒ |a| ∈ Errors

slide-56
SLIDE 56

More technical details Soundness proof

  • 1. Eliminating hides

We simulate computation with hiding in the language without hiding (and normal β-reduction) by let-extruding hiding constructs. δ(b, φ. E[hide φ in a ] ) ֒ → let x = abs(E, a) in δ(b, φ. E[app(x, E) ]) If |a| is the ֒ → normal form of a: If a − → b then |a| − →∗ |b|. a ∈ Errors ⇐ ⇒ |a| ∈ Errors

  • 2. Soundness of the language without hide

Bisimulation with Fcc (variant with both consistent and inconsistent abstractions, but no inconsistent assumptions [P]) Fcc proved sound with a semantics approach. Direct soundness proof should be possible.

slide-57
SLIDE 57

Consistent assumptions

I focused on (possibly) inconsistent assumptions, but consistent assumptions are also common and equally useful.

slide-58
SLIDE 58

Mixing consistent and inconsistent abstraction

We build a data-type α term that contains computations, of type α: type _ term = | TLam : ’a ∗ [ ’a = ’b → ’c ] → ’a term | TApp : (’b → ’a) term ∗ ’b term → ’a term let rec eval (type a) (t : a term) : a = match t with | TLam (f, z) → δ(z, φ. f) | TApp (tf, tx) → (eval tf) (eval tx) The constructor TLam constraints ’a to be an arrow type. A value TLam (f, w) carries a witness z that f has an arrow type. The constructor TApp is surjective, so it needs not block the evaluation.

slide-59
SLIDE 59

Implicit types

Our calculus is implicitly-typed ⊙ This simplifies the presentation ⊕ We focus on computation and soundness issues ⊕ Terms only contain computational constructs (i.e. that determines the semantics) and no erasable features at all. ⊖ Does not provide a surface language.

slide-60
SLIDE 60

Take away

We may support inconsistent abstraction the presence of full reduction. One must allow to block (for soundness) and unblock (for confluence) reduction of subterms. We should distinguish consistent and inconsistent abstractions and use whichever is most appropriate. Language design could help preserve/structure this distinction. (Opinion) In many cases programmers also think of correctness in an abstract way, in terms of full reduction. We should also care for full reduction in the meta-theoretical study of programming languages.