Lean 语言参考

18.3. 句法🔗

Lean 支持通过特殊语法使用函子、应用函子和 monad 进行编程:

  • 中缀运算符适用于最常见的操作。

  • 一种名为 Lean.Parser.Term.do : termdo-notation 的嵌入式语言允许在 monad 中编写程序时使用命令式语法。

18.3.1. 中缀运算符🔗

中缀运算符主要在较小的表达式中或没有 Monad 实例时有用。

18.3.1.1. 函子🔗

Functor.map 有两个中缀运算符。

syntaxFunctor Operators

g <$> xFunctor.map g x 的缩写。

term ::= ...
    | Applies a function inside a functor. This is used to overload the `<$>` operator.

When mapping a constant function, use `Functor.mapConst` instead, because it may be more
efficient.


Conventions for notations in identifiers:

 * The recommended spelling of `<$>` in identifiers is `map`.term <$> term

x <&> gFunctor.map g x 的缩写。

term ::= ...
    | Maps a function over a functor, with parameters swapped so that the function comes last.

This function is `Functor.map` with the parameters reversed, typically used via the `<&>` operator.


Conventions for notations in identifiers:

 * The recommended spelling of `<&>` in identifiers is `mapRev`.term <&> term

18.3.1.2. 应用函子🔗

syntaxApplicative Operators

g <*> xSeq.seq g (fun () => x) 的缩写。 插入该函数是为了延迟计算,因为控制可能无法到达参数。

term ::= ...
    | The implementation of the `<*>` operator.

In a monad, `mf <*> mx` is the same as `do let f ← mf; x ← mx; pure (f x)`: it evaluates the
function first, then the argument, and applies one to the other.

To avoid surprising evaluation semantics, `mx` is taken "lazily", using a `Unit → f α` function.


Conventions for notations in identifiers:

 * The recommended spelling of `<*>` in identifiers is `seq`.term <*> term

e1 *> e2SeqRight.seqRight e1 (fun () => e2) 的缩写。

term ::= ...
    | Sequences the effects of two terms, discarding the value of the first. This function is usually
invoked via the `*>` operator.

Given `x : f α` and `y : f β`, `x *> y` runs `x`, then runs `y`, and finally returns the result of
`y`.

The evaluation of the second argument is delayed by wrapping it in a function, enabling
“short-circuiting” behavior from `f`.


Conventions for notations in identifiers:

 * The recommended spelling of `*>` in identifiers is `seqRight`.term *> term

e1 <* e2SeqLeft.seqLeft e1 (fun () => e2) 的缩写。

term ::= ...
    | Sequences the effects of two terms, discarding the value of the second. This function is usually
invoked via the `<*` operator.

Given `x : f α` and `y : f β`, `x <* y` runs `x`, then runs `y`, and finally returns the result of
`x`.

The evaluation of the second argument is delayed by wrapping it in a function, enabling
“short-circuiting” behavior from `f`.


Conventions for notations in identifiers:

 * The recommended spelling of `<*` in identifiers is `seqLeft`.term <* term

许多应用函子还通过 Alternative 类型类支持故障和恢复。 此类还有一个中缀运算符。

syntaxAlternative Operators

e <|> e'OrElse.orElse e (fun () => e') 的缩写。 插入该函数是为了延迟计算,因为控制可能无法到达参数。

term ::= ...
    | `a <|> b` executes `a` and returns the result, unless it fails in which
case it executes and returns `b`. Because `b` is not always executed, it
is passed as a thunk so it can be forced only when needed.
The meaning of this notation is type-dependent. 

Conventions for notations in identifiers:

 * The recommended spelling of `<|>` in identifiers is `orElse`.term <|> term
structure User where name : String favoriteNat : Nat def main : IO Unit := pure ()
Infix Functor and Applicative Operators

常见的函数式编程习惯是在某些上下文中通过 Functor.mapSeq.seq 应用纯函数来产生效果。 该函数使用 <$> 应用于其参数序列,并且参数由 <*> 分隔。

在此示例中,构造函数 User.mk 通过 main 主体中的此习惯用法进行应用。

def getName : IO String := do IO.println "What is your name?" return ( ( IO.getStdin).getLine).trimAsciiEnd.copy partial def getFavoriteNat : IO Nat := do IO.println "What is your favorite natural number?" let line ( IO.getStdin).getLine if let some n := line.trimAscii.copy.toNat? then return n else IO.println "Let's try again." getFavoriteNat structure User where name : String favoriteNat : Nat deriving Repr def main : IO Unit := do let user User.mk <$> getName <*> getFavoriteNat IO.println (repr user)

使用此输入运行时:

stdinA. Lean UserNone42

它产生这样的输出:

stdoutWhat is your name?What is your favorite natural number?Let's try again.What is your favorite natural number?{ name := "A. Lean User", favoriteNat := 42 }

18.3.1.3. 单子🔗

Monad 主要通过 Lean.Parser.Term.do : termdo-notation 使用。 然而,有时通过运算符描述一元计算会很方便。

syntaxMonad Operators

act >>= fBind.bind act f 的语法。

term ::= ...
    | Sequences two computations, allowing the second to depend on the value computed by the first.

If `x : m α` and `f : α → m β`, then `x >>= f : m β` represents the result of executing `x` to get
a value of type `α` and then passing it to `f`.


Conventions for notations in identifiers:

 * The recommended spelling of `>>=` in identifiers is `bind`.term >>= term

类似地,反转运算符 f =<< actBind.bind act f 的语法。

term ::= ...
    | Same as `Bind.bind` but with arguments swapped. 

Conventions for notations in identifiers:

 * The recommended spelling of `=<<` in identifiers is `bindLeft`.term =<< term

Kleisli 组合运算符 Bind.kleisliRightBind.kleisliLeft 也有中缀运算符。

term ::= ...
    | Left-to-right composition of Kleisli arrows. 

Conventions for notations in identifiers:

 * The recommended spelling of `>=>` in identifiers is `kleisliRight`.term >=> term
term ::= ...
    | Right-to-left composition of Kleisli arrows. 

Conventions for notations in identifiers:

 * The recommended spelling of `<=<` in identifiers is `kleisliLeft`.term <=< term

18.3.2. do-符号🔗

Monad 主要通过 Lean.Parser.Term.do : termdo-notation 使用,这是一种用于命令式编程的嵌入式语言。 它提供了熟悉的语法来排序有效的操作、提前返回、局部可变变量、循环和异常处理。 所有这些功能都转换为 Monad 类型类的操作,其中一些功能需要添加指定容器迭代的类实例,例如 ForIn。 有关 Lean.Parser.Term.do : termdo 表示法设计的更多详细信息,请参阅 Ullrich and de Moura (2022)Sebastian Ullrich and Leonardo de Moura, 2022. do Unchained: Embracing Local Imperativity in a Purely Functional Language”. In Proceedings of the ACM on Programming Languages: ICFP 2022.

Lean.Parser.Term.do : termdo 项由关键字 Lean.Parser.Term.do : termdo 后跟 Lean.Parser.Term.do : termdo elements 序列组成。

syntaxdo-Notation
term ::= ...
    | do doSeqItem*

Lean.Parser.Term.do : termdo 中的元素可以用分号分隔;否则,每个都应该在自己的行上,并且它们应该具有相同的缩进。

18.3.2.1. 顺序计算🔗

Lean.Parser.Term.do : termdo-element 的一种形式是术语。

syntaxTerms in do-Notation
doSeqItem ::= ...
    | term

后跟元素序列的术语被翻译为 bind 的使用;特别是,do e1; es 被转换为 e1 >>= fun () => do es

Lean.Parser.Term.do : termdo 元件

脱糖

do e1 ese1 >>= fun () => do es

该项的计算结果也可以被命名,以便在后续步骤中使用它。 这是使用 Lean.Parser.Term.doLet : doElemlet 完成的。

syntaxData Dependence in do-Notation

Lean.Parser.Term.do : termdo 块中有两种形式的一元 Lean.Parser.Term.doLet : doElemlet 绑定。 第一个将标识符绑定到结果,并带有可选的类型注释:

doSeqItem ::= ...
    | let Configuration options for `let` tactics. ident(:term)?  term

第二个将模式绑定到结果。 以 | 开头的后备子句指定模式与结果不匹配时的行为。

doSeqItem ::= ...
    | let Configuration options for `let` tactics. term  term
        (| doSeqIndent)?

此语法也被转换为 bind 的使用。 do let x e1; es 转换为 e1 >>= fun x => do es,后备子句转换为默认模式匹配。 Lean.Parser.Term.doLet : doElemlet 也可以与标准定义语法 := 一起使用,而不是与 一起使用。 这表明这是一个纯粹的定义,而不是一元的定义:

do let x := e; es 转换为 let x := e; do es

Lean.Parser.Term.do : termdo 元件

脱糖

do let x e1 ese1 >>= fun x => do es
do let some x e1? | fallback ese1? >>= fun | some x => do es | _ => fallback
do let x := e eslet x := e do es

Lean.Parser.Term.do : termdo 块内, 可以用作前缀运算符。 应用它的表达式将替换为新变量,该变量在当前步骤之前使用 bind 进行绑定。 这允许在原本可能期望纯值的位置使用单子效果,同时仍然保持描述有效计算和实际执行其效果之间的区别。 多次出现的 按从左到右、从内到外的顺序进行处理。

示例 Lean.Parser.Term.do : termdo 元素

脱糖

do f ( e1) ( e2) esdo let x e1 let y e2 f x y es
do let x := g ( h ( e1)) esdo let y e1 let z h y let x := g z es
Example Nested Action Desugarings

除了方便地支持具有数据依赖性的顺序计算之外,Lean.Parser.Term.do : termdo-notation 还支持本地添加各种效果,包括提前返回、本地可变状态和提前终止的循环。 这些效果是通过整个 Lean.Parser.Term.do : termdo 块以类似于 monad 转换器 的方式进行转换来实现的,而不是通过局部脱糖来实现。

18.3.2.2. 提前返回🔗

提前返回会立即终止给定值的计算。 该值是从最接近的包含 Lean.Parser.Term.do : termdo 的块返回的;但是,这可能不是最接近的 do 关键字。 在其自己的部分中描述了确定 Lean.Parser.Term.do : termdo 块范围的规则。

syntaxEarly Return
doSeqItem ::= ...
    | `return e` inside of a `do` block makes the surrounding block evaluate to `pure e`,
skipping any further statements.
Note that uses of the `do` keyword in other syntax like in `for _ in _ do`
do not constitute a surrounding block in this sense;
in supported editors, the corresponding `do` keyword of the surrounding block
is highlighted when hovering over `return`.

`return` not followed by a term starting on the same line is equivalent to `return ()`.
return term
doSeqItem ::= ...
    | `return e` inside of a `do` block makes the surrounding block evaluate to `pure e`,
skipping any further statements.
Note that uses of the `do` keyword in other syntax like in `for _ in _ do`
do not constitute a surrounding block in this sense;
in supported editors, the corresponding `do` keyword of the surrounding block
is highlighted when hovering over `return`.

`return` not followed by a term starting on the same line is equivalent to `return ()`.
return

并非所有 monad 都包含提前返回。 因此,当Lean.Parser.Term.do : termdo块包含Lean.Parser.Term.doReturn : doElem`return e` inside of a `do` block makes the surrounding block evaluate to `pure e`, skipping any further statements. Note that uses of the `do` keyword in other syntax like in `for _ in _ do` do not constitute a surrounding block in this sense; in supported editors, the corresponding `do` keyword of the surrounding block is highlighted when hovering over `return`. `return` not followed by a term starting on the same line is equivalent to `return ()`. return时,需要重写代码来模拟效果。 使用早期返回来计算单子 m 中类型 α 的值的程序可以被视为单子 ExceptT α m α 中的程序:早期返回值采用异常路径,而普通返回则不采用异常路径。 然后,外部处理程序可以从任一代码路径返回值。 在内部,Lean.Parser.Term.do : termdo精化器执行的转换与此非常相似。

就其本身而言,Lean.Parser.Term.doReturn : doElem`return e` inside of a `do` block makes the surrounding block evaluate to `pure e`, skipping any further statements. Note that uses of the `do` keyword in other syntax like in `for _ in _ do` do not constitute a surrounding block in this sense; in supported editors, the corresponding `do` keyword of the surrounding block is highlighted when hovering over `return`. `return` not followed by a term starting on the same line is equivalent to `return ()`. returnLean.Parser.Term.doReturn : doElem`return e` inside of a `do` block makes the surrounding block evaluate to `pure e`, skipping any further statements. Note that uses of the `do` keyword in other syntax like in `for _ in _ do` do not constitute a surrounding block in this sense; in supported editors, the corresponding `do` keyword of the surrounding block is highlighted when hovering over `return`. `return` not followed by a term starting on the same line is equivalent to `return ()`. return () 的缩写。

18.3.2.3. 局部可变状态🔗

本地可变状态是无法转义定义它的 Lean.Parser.Term.do : termdo 块的可变状态。 Lean.Parser.Term.doLet : doElemlet mut 绑定器引入了本地可变绑定。

syntaxLocal Mutability

可变绑定可以通过纯计算或一元计算来初始化:

doSeqItem ::= ...
    | let mut Configuration options for `let` tactics. `letDecl` matches the body of a let declaration `let f x1 x2 := e`,
`let pat := e` (where `pat` is an arbitrary term) or `let f | pat1 => e1 | pat2 => e2 ...`
(a pattern matching declaration), except for the `let` keyword itself.
`let rec` declarations are not handled here. (ident | A *hole* (or *placeholder term*), which stands for an unknown term that is expected to be inferred based on context.
For example, in `@id _ Nat.zero`, the `_` must be the type of `Nat.zero`, which is `Nat`.

The way this works is that holes create fresh metavariables.
The elaborator is allowed to assign terms to metavariables while it is checking definitional equalities.
This is often known as *unification*.

Normally, all holes must be solved for. However, there are a few contexts where this is not necessary:
* In `match` patterns, holes are catch-all patterns.
* In some tactics, such as `refine'` and `apply`, unsolved-for placeholders become new goals.

Related concept: implicit parameters are automatically filled in with holes during the elaboration process.

See also `?m` syntax (synthetic holes).
hole) := term
doSeqItem ::= ...
    | let mut Configuration options for `let` tactics. ident  doElem

类似地,它们可以用纯值或 monad 计算的结果进行变异:

doElem ::= ...
    | ident(: term)?  := term
doElem ::= ...
    | term(: term)? := term
doElem ::= ...
    | ident(: term)?  term
doElem ::= ...
    | term  term
        (| doSeqIndent)?

这些本地可变的绑定不如 state monad 强大,因为它们在词法范围之外是不可变的;这也让他们更容易推理。 当 Lean.Parser.Term.do : termdo 块包含可变绑定时,Lean.Parser.Term.do : termdo精化器会以类似于 StateT 的方式转换表达式,构造一个新的 monad 并使用正确的值对其进行初始化。

18.3.2.4. 控制结构🔗

有一些 Lean.Parser.Term.do : termdo 元素对应于大多数 Lean 的术语级控制结构。 当它们作为 Lean.Parser.Term.do : termdo 块中的步骤出现时,它们被解释为 Lean.Parser.Term.do : termdo 元素而不是术语。 控制结构的每个分支都是 Lean.Parser.Term.do : termdo 元素的序列,而不是术语,其中一些分支在语法上比相应的术语更灵活。

从语法上讲,Lean.Parser.Term.doIf : doElemthen 分支不能省略。 对于这些情况,Lean.Parser.Term.doUnless : doElemunless 仅在条件为 false 时执行其主体。 Lean.Parser.Term.doUnless : doElemunless 中的 Lean.Parser.Term.do : termdo 是其语法的一部分,不会产生嵌套的 Lean.Parser.Term.do : termdo 块。

syntaxReverse Conditionals
doSeqItem ::= ...
    | unless term do
        doSeqItem*

Lean.Parser.Term.doMatch : doElemmatch 用于 Lean.Parser.Term.do : termdo 块时,每个分支都被视为同一块的一部分。 否则,它相当于 Lean.Parser.Term.match : termPattern matching. `match e, ... with | p, ... => f | ...` matches each given term `e` against each pattern `p` of a match alternative. When all patterns of an alternative match, the `match` term evaluates to the value of the corresponding right-hand side `f` with the pattern variables bound to the respective matched values. If used as `match h : e, ... with | p, ... => f | ...`, `h : e = p` is available within `f`. When not constructing a proof, `match` does not automatically substitute variables matched on in dependent variables' types. Use `match (generalizing := true) ...` to enforce this. Syntax quotations can also be used in a pattern match. This matches a `Syntax` value against quotations, pattern variables, or `_`. Quoted identifiers only match identical identifiers - custom matching such as by the preresolved names only should be done explicitly. `Syntax.atom`s are ignored during matching by default except when part of a built-in literal. For users introducing new atoms, we recommend wrapping them in dedicated syntax kinds if they should participate in matching. For example, in ```lean syntax "c" ("foo" <|> "bar") ... ``` `foo` and `bar` are indistinguishable during matching, but in ```lean syntax foo := "foo" syntax "c" (foo <|> "bar") ... ``` they are not. match 项。

18.3.2.5. 迭代🔗

Lean.Parser.Term.do : termdo 块内,Lean.Parser.Term.doFor : doElem`for x in e do s` iterates over `e` assuming `e`'s type has an instance of the `ForIn` typeclass. `break` and `continue` are supported inside `for` loops. `for x in e, x2 in e2, ... do s` iterates of the given collections in parallel, until at least one of them is exhausted. The types of `e2` etc. must implement the `Std.ToStream` typeclass. forLean.Parser.Term.doFor : doElem`for x in e do s` iterates over `e` assuming `e`'s type has an instance of the `ForIn` typeclass. `break` and `continue` are supported inside `for` loops. `for x in e, x2 in e2, ... do s` iterates of the given collections in parallel, until at least one of them is exhausted. The types of `e2` etc. must implement the `Std.ToStream` typeclass. in 循环允许对数据结构进行迭代。 循环体是包含 Lean.Parser.Term.do : termdo 块的一部分,因此可以使用局部效果,例如提前返回和可变变量。

syntaxIteration over Collections
doSeqItem ::= ...
    | `for x in e do s`  iterates over `e` assuming `e`'s type has an instance of the `ForIn` typeclass.
`break` and `continue` are supported inside `for` loops.
`for x in e, x2 in e2, ... do s` iterates of the given collections in parallel,
until at least one of them is exhausted.
The types of `e2` etc. must implement the `Std.ToStream` typeclass.
for ((ident :)? term in term),* do
        doSeqItem*

Lean.Parser.Term.doFor : doElem`for x in e do s` iterates over `e` assuming `e`'s type has an instance of the `ForIn` typeclass. `break` and `continue` are supported inside `for` loops. `for x in e, x2 in e2, ... do s` iterates of the given collections in parallel, until at least one of them is exhausted. The types of `e2` etc. must implement the `Std.ToStream` typeclass. forLean.Parser.Term.doFor : doElem`for x in e do s` iterates over `e` assuming `e`'s type has an instance of the `ForIn` typeclass. `break` and `continue` are supported inside `for` loops. `for x in e, x2 in e2, ... do s` iterates of the given collections in parallel, until at least one of them is exhausted. The types of `e2` etc. must implement the `Std.ToStream` typeclass. in 循环至少需要一个子句来指定要执行的迭代,该子句由一个可选的成员资格证明名称后跟一个冒号 (:)、一个要绑定的模式、关键字 Lean.Parser.Term.doFor : doElem`for x in e do s` iterates over `e` assuming `e`'s type has an instance of the `ForIn` typeclass. `break` and `continue` are supported inside `for` loops. `for x in e, x2 in e2, ... do s` iterates of the given collections in parallel, until at least one of them is exhausted. The types of `e2` etc. must implement the `Std.ToStream` typeclass. in 和一个集合术语组成。 该模式可能只是 identifier,必须与集合中的任何元素匹配;此位置的模式不能用作隐式过滤器。 可以通过用逗号分隔来提供进一步的子句。 每个集合都会同时迭代,当任何一个集合用完元素时,迭代就会停止。

Iteration Over Multiple Collections

迭代多个集合时,当任何集合用完元素时,迭代就会停止。

#[(0, 'a'), (1, 'b')]#eval Id.run do let mut v := #[] for x in [0:43], y in ['a', 'b'] do v := v.push (x, y) return v
#[(0, 'a'), (1, 'b')]
Iteration over Array Indices with Lean.Parser.Term.doFor : doElem`for x in e do s` iterates over `e` assuming `e`'s type has an instance of the `ForIn` typeclass. `break` and `continue` are supported inside `for` loops. `for x in e, x2 in e2, ... do s` iterates of the given collections in parallel, until at least one of them is exhausted. The types of `e2` etc. must implement the `Std.ToStream` typeclass. for

当使用 Lean.Parser.Term.doFor : doElem`for x in e do s` iterates over `e` assuming `e`'s type has an instance of the `ForIn` typeclass. `break` and `continue` are supported inside `for` loops. `for x in e, x2 in e2, ... do s` iterates of the given collections in parallel, until at least one of them is exhausted. The types of `e2` etc. must implement the `Std.ToStream` typeclass. for 迭代数组的有效索引时,命名成员资格证明允许策略成功搜索数组索引在界限内的证明。

def satisfyingIndices (p : α Prop) [DecidablePred p] (xs : Array α) : Array Nat := Id.run do let mut out := #[] for h : i in [0:xs.size] do if p xs[i] then out := out.push i return out

省略假设名称会导致数组查找失败,因为在上下文中无法证明迭代变量在指定范围内。

for 循环的迭代被转化为 ForIn.forIn 的使用,它是 ForM.forM 的类似物,增加了对局部突变和提前终止的支持。 ForIn.forIn 接收本地可变状态的初始值和一元操作作为参数,以及迭代的集合。 传递给 ForIn.forIn 的单子操作将当前状态作为参数,并在单子 m 中执行操作后,返回 ForInStep.yield 以指示迭代应使用一组更新的本地可变值继续,或者返回 ForInStep.done 以指示 Lean.Parser.Term.doBreak : doElem`break` exits the surrounding `for` loop. breakLean.Parser.Term.doReturn : doElem`return e` inside of a `do` block makes the surrounding block evaluate to `pure e`, skipping any further statements. Note that uses of the `do` keyword in other syntax like in `for _ in _ do` do not constitute a surrounding block in this sense; in supported editors, the corresponding `do` keyword of the surrounding block is highlighted when hovering over `return`. `return` not followed by a term starting on the same line is equivalent to `return ()`. return被执行。 迭代完成后,ForIn.forIn 返回局部可变值的最终值。

循环的具体脱糖取决于循环体中如何使用状态和提前终止。 以下是一些示例:

Lean.Parser.Term.do : termdo 元件

脱糖

do let mut b := for x in xs do b f x b esdo let b := let b ForIn.forIn xs b fun x b => do let b f x b return ForInStep.yield b es
do let mut b := for x in xs do b f x b break esdo let b := let b ForIn.forIn xs b fun x b => do let b f x b return ForInStep.done b es
do let mut b := for h : x in xs do b f' x h b esdo let b := let b ForIn'.forIn' xs b fun x h b => do let b f' x h b return ForInStep.yield b es
do let mut b := for h : x in xs do b f' x h b break esdo let b := let b ForIn'.forIn' xs b fun x h b => do let b f' x h b return ForInStep.done b es

当条件保持为真时,Lean.doElemWhile_Do_while 循环的主体会重复。 可以在未标记为 Lean.Parser.Command.declaration : commandpartial 的函数中使用它们编写无限循环。 这是因为 Lean.Parser.Command.declaration : commandpartial 修饰符仅适用于由正在定义的函数引起的非终止或无限回归,而不是由它调用的函数引起的。 Lean.doElemWhile_Do_while 循环的翻译依赖于单独的帮助程序。

Lean.doElemRepeat__Until_repeat-Lean.doElemRepeat__Until_until 循环体始终至少执行一次。 每次迭代后,都会检查条件,并在条件为“假”时重复循环。 当条件成立时,迭代停止。

syntaxPost-Tested Loops
doSeqItem ::= ...
    | repeat
        doSeqItem*
      until term

重复 Lean.doElemRepeat_repeat 循环体,直到执行 Lean.Parser.Term.doBreak : doElem`break` exits the surrounding `for` loop. break 语句为止。 就像 Lean.doElemWhile_Do_while 循环一样,这些循环可以在未标记为 Lean.Parser.Command.declaration : commandpartial 的函数中使用。

syntaxUnconditional Loops
doSeqItem ::= ...
    | repeat
        doSeqItem*

Lean.Parser.Term.doContinue : doElem`continue` skips to the next iteration of the surrounding `for` loop. continue 语句跳过最接近的封闭 Lean.doElemRepeat_repeatLean.doElemWhile_Do_whileLean.Parser.Term.doFor : doElem`for x in e do s` iterates over `e` assuming `e`'s type has an instance of the `ForIn` typeclass. `break` and `continue` are supported inside `for` loops. `for x in e, x2 in e2, ... do s` iterates of the given collections in parallel, until at least one of them is exhausted. The types of `e2` etc. must implement the `Std.ToStream` typeclass. for 循环体的其余部分,继续进行下一次迭代。 Lean.Parser.Term.doBreak : doElem`break` exits the surrounding `for` loop. break 语句终止最接近的封闭 Lean.doElemRepeat_repeatLean.doElemWhile_Do_whileLean.Parser.Term.doFor : doElem`for x in e do s` iterates over `e` assuming `e`'s type has an instance of the `ForIn` typeclass. `break` and `continue` are supported inside `for` loops. `for x in e, x2 in e2, ... do s` iterates of the given collections in parallel, until at least one of them is exhausted. The types of `e2` etc. must implement the `Std.ToStream` typeclass. for 循环,从而停止迭代。

syntaxLoop Control Statements
doSeqItem ::= ...
    | `continue` skips to the next iteration of the surrounding `for` loop. continue
doSeqItem ::= ...
    | `break` exits the surrounding `for` loop. break

除了 Lean.Parser.Term.doBreak : doElem`break` exits the surrounding `for` loop. break 之外,循环始终可以通过当前 monad 中的效果来终止。 从循环中抛出异常会终止循环。

Terminating Loops in the Option Monad

Alternative 类中的 failure 方法可用于终止 Option monad 中原本无限的循环。

none#eval show Option Nat from do let mut i := 0 repeat if i > 1000 then failure else i := 2 * (i + 1) return i
none

18.3.2.6. 识别 do🔗

Lean.Parser.Term.do : termdo 表示法的许多功能都会对 当前 Lean.Parser.Term.do : termdo产生影响。 特别是,提前返回会中止当前块,导致其计算返回值,并且可变绑定只能在定义它们的块中进行更改。 理解这些特征需要精确定义“同一”块的含义。

根据经验,这可以使用 Lean 语言服务器进行检查。 当光标位于 Lean.Parser.Term.doReturn : doElem`return e` inside of a `do` block makes the surrounding block evaluate to `pure e`, skipping any further statements. Note that uses of the `do` keyword in other syntax like in `for _ in _ do` do not constitute a surrounding block in this sense; in supported editors, the corresponding `do` keyword of the surrounding block is highlighted when hovering over `return`. `return` not followed by a term starting on the same line is equivalent to `return ()`. return 语句上时,相应的 Lean.Parser.Term.do : termdo 关键字会突出显示。 尝试改变同一 Lean.Parser.Term.do : termdo 块之外的可变绑定会导致错误消息。

从 return 中突出显示 do

突出显示带有错误的返回 do

Highlighting Lean.Parser.Term.do : termdo

规则如下:

  • 立即嵌套在开始块的 Lean.Parser.Term.do : termdo 关键字下的每个元素都属于该块。

  • 直接嵌套在 Lean.Parser.Term.do : termdo 关键字下的每个元素(包含 Lean.Parser.Term.do : termdo 块中的元素)都属于外部块。

  • Lean.Parser.Term.doIf : doElemifLean.Parser.Term.doMatch : doElemmatchLean.Parser.Term.doUnless : doElemunless 元素的分支中的元素与包含它们的控制结构属于同一 Lean.Parser.Term.do : termdo 块。作为 Lean.Parser.Term.doUnless : doElemunless 语法一部分的 Lean.Parser.Term.doUnless : doElemdo 关键字不会引入新的 Lean.Parser.Term.do : termdo 块。

  • Lean.doElemRepeat_repeatLean.doElemWhile_Do_whileLean.Parser.Term.doFor : doElem`for x in e do s` iterates over `e` assuming `e`'s type has an instance of the `ForIn` typeclass. `break` and `continue` are supported inside `for` loops. `for x in e, x2 in e2, ... do s` iterates of the given collections in parallel, until at least one of them is exhausted. The types of `e2` etc. must implement the `Std.ToStream` typeclass. for 主体中的元素与包含它们的循环属于同一 Lean.Parser.Term.do : termdo 块。作为 Lean.doElemWhile_Do_whileLean.Parser.Term.doFor : doElem`for x in e do s` iterates over `e` assuming `e`'s type has an instance of the `ForIn` typeclass. `break` and `continue` are supported inside `for` loops. `for x in e, x2 in e2, ... do s` iterates of the given collections in parallel, until at least one of them is exhausted. The types of `e2` etc. must implement the `Std.ToStream` typeclass. for 语法一部分的 Lean.Parser.Term.doFor : doElem`for x in e do s` iterates over `e` assuming `e`'s type has an instance of the `ForIn` typeclass. `break` and `continue` are supported inside `for` loops. `for x in e, x2 in e2, ... do s` iterates of the given collections in parallel, until at least one of them is exhausted. The types of `e2` etc. must implement the `Std.ToStream` typeclass. do 关键字不会引入新的 Lean.Parser.Term.do : termdo 块。

Nested do and Branches

以下示例输出 6 而不是 7

def test : StateM Nat Unit := do set 5 if true then set 6 do return set 7 return ((), 6)#eval test.run 0
((), 6)

这是因为 Lean.Parser.Term.doIf : doElemif 下的 Lean.Parser.Term.doReturn : doElem`return e` inside of a `do` block makes the surrounding block evaluate to `pure e`, skipping any further statements. Note that uses of the `do` keyword in other syntax like in `for _ in _ do` do not constitute a surrounding block in this sense; in supported editors, the corresponding `do` keyword of the surrounding block is highlighted when hovering over `return`. `return` not followed by a term starting on the same line is equivalent to `return ()`. return 语句与其直接父级属于同一 Lean.Parser.Term.do : termdo,而该父级本身又与 Lean.Parser.Term.doIf : doElemif 属于同一 Lean.Parser.Term.do : termdo。 如果作为其他 Lean.Parser.Term.do : termdo 块中的元素出现的 Lean.Parser.Term.do : termdo 块改为创建新块,则该示例将输出 7

18.3.2.7. Type 用于迭代的类🔗

要与没有成员资格证明的 Lean.Parser.Term.doFor : doElem`for x in e do s` iterates over `e` assuming `e`'s type has an instance of the `ForIn` typeclass. `break` and `continue` are supported inside `for` loops. `for x in e, x2 in e2, ... do s` iterates of the given collections in parallel, until at least one of them is exhausted. The types of `e2` etc. must implement the `Std.ToStream` typeclass. for 循环一起使用,集合必须实现 ForIn 类型类。 实现 ForIn' 还允许使用具有成员资格证明的 Lean.Parser.Term.doFor : doElem`for x in e do s` iterates over `e` assuming `e`'s type has an instance of the `ForIn` typeclass. `break` and `continue` are supported inside `for` loops. `for x in e, x2 in e2, ... do s` iterates of the given collections in parallel, until at least one of them is exhausted. The types of `e2` etc. must implement the `Std.ToStream` typeclass. for 循环。

🔗type class
ForIn.{u, v, u₁, u₂} (m : Type u₁ Type u₂) (ρ : Type u) (α : outParam (Type v)) : Type (max (max (max u (u₁ + 1)) u₂) v)
ForIn.{u, v, u₁, u₂} (m : Type u₁ Type u₂) (ρ : Type u) (α : outParam (Type v)) : Type (max (max (max u (u₁ + 1)) u₂) v)

Monadic iteration in do-blocks, using the for x in xs notation.

The parameter m is the monad of the do-block in which iteration is performed, ρ is the type of the collection being iterated over, and α is the type of elements.

Instance Constructor

ForIn.mk.{u, v, u₁, u₂}

Methods

forIn : {β : Type u₁}  ρ  β  (α  β  m (ForInStep β))  m β

Monadically iterates over the contents of a collection xs, with a local state b and the possibility of early termination.

Because a do block supports local mutable bindings along with return, and break, the monadic action passed to ForIn.forIn takes a starting state in addition to the current element of the collection and returns an updated state together with an indication of whether iteration should continue or terminate. If the action returns ForInStep.done, then ForIn.forIn should stop iteration and return the updated state. If the action returns ForInStep.yield, then ForIn.forIn should continue iterating if there are further elements, passing the updated state to the action.

More information about the translation of for loops into ForIn.forIn is available in the Lean reference manual.

🔗type class
ForIn'.{u, v, u₁, u₂} (m : Type u₁ Type u₂) (ρ : Type u) (α : outParam (Type v)) (d : outParam (Membership α ρ)) : Type (max (max (max u (u₁ + 1)) u₂) v)
ForIn'.{u, v, u₁, u₂} (m : Type u₁ Type u₂) (ρ : Type u) (α : outParam (Type v)) (d : outParam (Membership α ρ)) : Type (max (max (max u (u₁ + 1)) u₂) v)

Monadic iteration in do-blocks with a membership proof, using the for h : x in xs notation.

The parameter m is the monad of the do-block in which iteration is performed, ρ is the type of the collection being iterated over, α is the type of elements, and d is the specific membership predicate to provide.

Instance Constructor

ForIn'.mk.{u, v, u₁, u₂}

Methods

forIn' : {β : Type u₁}  (x : ρ)  β  ((a : α)  a  x  β  m (ForInStep β))  m β

Monadically iterates over the contents of a collection xs, with a local state b and the possibility of early termination. At each iteration, the body of the loop is provided with a proof that the current element is in the collection.

Because a do block supports local mutable bindings along with return, and break, the monadic action passed to ForIn'.forIn' takes a starting state in addition to the current element of the collection with its membership proof. The action returns an updated state together with an indication of whether iteration should continue or terminate. If the action returns ForInStep.done, then ForIn'.forIn' should stop iteration and return the updated state. If the action returns ForInStep.yield, then ForIn'.forIn' should continue iterating if there are further elements, passing the updated state to the action.

More information about the translation of for loops into ForIn'.forIn' is available in the Lean reference manual.

🔗inductive type
ForInStep.{u} (α : Type u) : Type u
ForInStep.{u} (α : Type u) : Type u

An indication of whether a loop's body terminated early that's used to compile the for x in xs notation.

A collection's ForIn or ForIn' instance describes how to iterate over its elements. The monadic action that represents the body of the loop returns a ForInStep α, where α is the local state used to implement features such as let mut.

Constructors

ForInStep.done.{u} {α : Type u} : α  ForInStep α

The loop should terminate early.

ForInStep.done is produced by uses of break or return in the loop body.

ForInStep.yield.{u} {α : Type u} : α  ForInStep α

The loop should continue with the next iteration, using the returned state.

ForInStep.yield is produced by continue and by reaching the bottom of the loop body.

🔗def
ForInStep.value.{u_1} {α : Type u_1} (x : ForInStep α) : α
ForInStep.value.{u_1} {α : Type u_1} (x : ForInStep α) : α

Extracts the value from a ForInStep, ignoring whether it is ForInStep.done or ForInStep.yield.

🔗type class
ForM.{u, v, w₁, w₂} (m : Type u Type v) (γ : Type w₁) (α : outParam (Type w₂)) : Type (max (max v w₁) w₂)
ForM.{u, v, w₁, w₂} (m : Type u Type v) (γ : Type w₁) (α : outParam (Type w₂)) : Type (max (max v w₁) w₂)

Overloaded monadic iteration over some container type.

An instance of ForM m γ α describes how to iterate a monadic operator over a container of type γ with elements of type α in the monad m. The element type should be uniquely determined by the monad and the container.

Use ForM.forIn to construct a ForIn instance from a ForM instance, thus enabling the use of the for operator in do-notation.

Instance Constructor

ForM.mk.{u, v, w₁, w₂}

Methods

forM : γ  (α  m PUnit)  m PUnit

Runs the monadic action f on each element of the collection coll.

🔗def
ForM.forIn.{u_1, u_2, u_3, u_4} {m : Type u_1 Type u_2} {β : Type u_1} {ρ : Type u_3} {α : Type u_4} [Monad m] [ForM (StateT β (ExceptT β m)) ρ α] (x : ρ) (b : β) (f : α β m (ForInStep β)) : m β
ForM.forIn.{u_1, u_2, u_3, u_4} {m : Type u_1 Type u_2} {β : Type u_1} {ρ : Type u_3} {α : Type u_4} [Monad m] [ForM (StateT β (ExceptT β m)) ρ α] (x : ρ) (b : β) (f : α β m (ForInStep β)) : m β

Creates a suitable implementation of ForIn.forIn from a ForM instance.