openlang

比较 Lean4 中的 have 和 let 绑定

我发现下面两个表达式

#eval have amount : Nat := 5 ; amount*4

以及

#eval let amount : Nat := 5 ; amount*4

行为完全相同。证明定理时也是这样。 它们可以互换吗?区别在哪里?

从英语翻译查看原文

后续讨论 3

顺着彼此的依据读下去
  1. 固定链接

    have 的工作方式如下

    在内部,have h : p := s; t 会成为项 (fun (h : p) => t) s。换句话说,s 证明 p,而 t 在假设 h : p 下证明所需结论,然后通过 lambda 抽象和应用把两者结合起来。

    let 的工作方式如下

    let 结构提供了更强的缩写方式,因此有些 let a := t1; t2 形式的表达式无法表示为 (fun a => t2) t1。可以练习思考一下,为什么下面的 keepType 定义能通过类型检查,而 loseType 不能。

    def keepType := let chosen := Nat; fun item : chosen => item + 4
    /-
      def loseType := (fun chosen => fun item : chosen => item + 4) Nat
    -/
    从英语翻译查看原文
  2. 固定链接

    区别在于 let 保留定义,而 have 会丢弃它。

    例如,在 Lean 4 中,下面的例子使用 let 可以通过,但换成 have 就会失败。

    example : {item : Nat // item = 0} := let item := 0 ; ⟨item, rfl⟩

    通常,证明使用 have,其他情况使用 let。在 Lean3 的策略模式中,可以用 dsimp [x] 展开 let x := ... 的定义,但我不知道 Lean4 中对应的做法。

    从英语翻译查看原文
  3. 固定链接

    看一个更实用的例子:

    def divisibleByTwo (number : Nat) : Prop :=
      ∃ factor : Nat, number = factor * 2
    
    theorem doubled_half (number : Nat) : divisibleByTwo (number / 2 * 2) :=
      let half := number / 2
      have : number / 2 * 2 = half * 2 := rfl
      Exists.intro half this
    
    theorem doubled_failure (number : Nat) : divisibleByTwo (number / 2 * 2) :=
      have half := number / 2
      have : number / 2 * 2 = half * 2 := rfl -- error: type mismatch
      Exists.intro half this

    第一个证明可以通过,而第二个在 rfl 表达式处失败。这是因为 let 把后续出现的所有 half 都替换为定义 number / 2。然而,使用 have 后,编译器只保留 half 的类型是 Nat 这一信息,并丢弃其定义,因此 rfl 无法把 number / 2 * 2 与任意的 half / 2 等同起来。

    所以我们通常用 have 引入中间命题或引理,因为类型系统只需要知道某个命题有证明,并不关心实际的证明过程。相反,当后面的证明需要变量的定义时,我们会用 let 引入变量。

    从英语翻译查看原文

同一个问题的其他角度

  1. 怎样继承另一个类里面的类?

    你好, 在一个我无法修改代码的包里,类的层次结构如下。 我想给 Inner 加个方便使用的包装,但因为 Inner 依赖外层实例,目前只做到把它放进外层类的子类里面。 可惜,在多个地方继承 Outer,就意味着每个新子类里都得重复这个方便使用的 ca…

    回复 1最近活动 scala
  2. 初始化以字符串数组为值的 TypeScript Map

    我在创建一个 Map,其中每个条目都保存字符串数组。怎样在一条语句中同时声明类型并完成初始化? 我(天真地)这样试了一下。 结果是下面这个错误。

    回复 2最近活动 typescript
  3. Typescript 可调用函数的约定

    为什么 Typescript 接受我的函数定义,虽然它与接口签名不同,但当我实际调用同一个函数时却报告问题。

    回复 2最近活动 typescript