Skip to content

RFC: pp using forall notation for all pi types from a Type to a Prop #1834

@kmill

Description

@kmill

The following shows the current way that many common variations on pi types are pretty printed:

variable (α : Nat → Type) (p q : Prop) (P : Nat → Prop)

#check (i : Nat) → Nat  -- Nat → Nat
#check {i : Nat} → Nat  -- {i : Nat} → Nat
#check (h : p) → q      -- p → q
#check (i : Nat) → p    -- Nat → p
#check (i : Nat) → α i  -- (i : Nat) → α i
#check {i : Nat} → α i  -- {i : Nat} → α i
#check (i : Nat) → P i  -- ∀ (i : Nat), P i
#check {i : Nat} → P i  -- ∀ {i : Nat}, P i
#check {i : Nat} → p    -- ∀ {i : Nat}, p

This RFC is primarily about the Nat → p case, which tends to be the most surprising (it is a source of questions on Zulip). This sort of Prop-valued pi type frequently appears in the course of rewrites, where a forall quantifier becomes non-dependent:

example (P : Nat → Prop) (q : Prop) (h : ∀ n, P n = q) (hq : q) :
  ∀ n, P n :=
by
  simp [h]
  -- ⊢ Nat → q
  exact λ _ => hq

The meaning of a non-dependent pi from a Type to a Prop is generally a universal quantification, so the pretty printer should represent such pi types in some way such that this is clear.

Concretely, given ι : Sort u and α : ι → Sort v, then when u > 0 and v = 0 we should make sure to use when pretty printing the corresponding pi type for α, even if the pi type is nondependent.

The nondependent u = 0 and v = 0 case should be left alone since the implication arrow serves its purpose just fine.


There are a couple of additional considerations presentation-wise.

While pretty printing pi types using gives the user the information that it is Prop-valued, this leads to the user not being able to immediately see whether the quantifier is dependent or not; in discussion, it was brought up that this information is important for expert Lean users. One possible solution to this is to pretty print Nat → p as ∀ (_ : Nat), p (rather than ∀ (i : Nat), p) with a placeholder for the variable. This way one can immediately see it is a Prop-valued non-implication non-dependent pi type.

This leads to some inconsistency when it comes to non-default binders for other nondependent pi types. One might entertain using placeholders in every context where a non-dependent pi would print a binder. For example, {_ : Nat} → Nat rather than {i : Nat} → Nat. This does lead to loss of information that is important for (i := 22) argument notation. There are other solutions (such as using a tombstone, or using semantic coloring in an editor), or we can simply choose to tolerate any potential inconsistency here.

In a similar direction, for instance binders we currently have

#check (α : Type) → [DecidableEq α] → True
-- ∀ (α : Type) [inst : DecidableEq α], True

With suggestions above, this would pretty print as ∀ (α : Type) [_ : DecidableEq α], True since the instance binder is non-dependent, but one may as well pretty print it as ∀ (α : Type) [DecidableEq α], True since it gives the same amount of information while looking prettier.

(The Lean 3 version of this RFC is lean PR 770.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    P-lowWe are not planning to work on this issueRFCRequest for comments

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions