Tipos indutivos, recursão, estruturas e classes de tipos
Christiano Braga · Mestrado em Sistemas e Computação · IME
Baseado no Hitchhiker's Guide to Logical Verification (LoVe), capítulo 5.
Um tipo indutivo é a sua lista de construtores, e todo valor é construído aplicando-os, cada um de uma única maneira.
Para um tipo de dados, Lean gera quatro princípios: o recursor T.rec, o casesOn não recursivo, a injetividade de cada construtor e a disjunção de construtores distintos.
Nat.succ.injEq : ∀ (u v : ℕ), (u.succ = v.succ) = (u = v)#check @Nat.succ.injEq
Nat.succ.injEq : ∀ (u v : ℕ), (u.succ = v.succ) = (u = v)O recursor é a forma bruta da recursão estrutural e, lido sobre uma proposição, a indução estrutural, que a Aula 7 deriva.
O casamento de padrões e a tática cases são sintaxe de superfície que o compilador de equações e cases traduzem no recursor.
namespace Func
def usingRec (n : ℕ) : ℕ :=
Nat.rec (motive := fun _ => ℕ)
0 (fun _ ih => ih + 2) n
example : usingRec 3 = 6 := rfl
end Func
namespace Func
example (m n : ℕ)
(h : Nat.succ m = Nat.succ n) :
m = n := m:ℕn:ℕh:m.succ = n.succ⊢ m = n
All goals completed! 🐙
example (n : ℕ) :
Nat.succ n ≠ 0 :=
Nat.succ_ne_zero n
end Func
Cada chamada recursiva é sobre um argumento estruturalmente menor, então a definição termina e se traduz no recursor.
namespace Func
def fact : ℕ → ℕ
| 0 => 1
| n + 1 => (n + 1) * fact n
def fib : ℕ → ℕ
| 0 => 0
| 1 => 1
| n + 2 => fib n + fib (n + 1)
end Func
Uma definição total expõe a sua equação como teorema utilizável, então aceitar loopy = loopy + 1 provaria False. Lean rejeita tais definições; um laço genuíno precisa de partial def, que fica opaco.
A equação falsa dá False
namespace Func
opaque loopy : ℕ
axiom loopy_eq :
loopy = loopy + 1
theorem loopy_false : False := ⊢ False
h:loopy = loopy + 1⊢ False
All goals completed! 🐙
end Func
Rejeitada: nenhum argumento diminui
def loopForever {α : Type} :
List α → List α
| [] => []
| x :: xs =>
loopForever (x :: xs)
A saída de emergência termination_by fica para depois; esta aula é estrutural.
match e if trazem o casamento de padrões para um termo. Um match se traduz pelo recursor do tipo, e if ramifica sobre uma instância Decidable. Option empacota um resultado parcial.
namespace Func
def classify (n : ℕ) : String :=
match n with
| 0 => "zero"
| 1 => "one"
| _ => "many"
def isZero (n : ℕ) : Bool :=
if n = 0 then true else false
end Func
namespace Func
def pred? : ℕ → Option ℕ
| 0 => none
| n + 1 => some n
example : pred? 0 = none := rfl
end Func
Os padrões são tentados de cima para baixo; o coringa _ casa com qualquer coisa.
Uma estrutura é um tipo indutivo de único construtor com campos nomeados, e Lean deriva uma projeção para cada um.
namespace Func
structure Segment where
lo : ℤ
hi : ℤ
def width (s : Segment) : ℤ :=
s.hi - s.lo
def seg1 : Segment :=
{ lo := 1, hi := 5 }
end Func
namespace Func
example : seg1 = ⟨1, 5⟩ := rfl
example : width seg1 = 4 := ⊢ width seg1 = 4
All goals completed! 🐙
end Func
{ … } e ⟨…⟩ constroem o mesmo valor.
extends constrói um registro maior sobre um menor, e { r with … } copia um registro mudando um campo.
namespace Func
structure NamedSegment
extends Segment where
name : String
def ns1 : NamedSegment :=
{ lo := 0, hi := 2, name := "a" }
end Func
namespace Func
example : ns1.lo = 0 := ⊢ ns1.lo = 0 All goals completed! 🐙
example : ns1.name = "a" := rfl
def widen (s : Segment) : Segment :=
{ s with hi := s.hi + 1 }
end Func
Os campos herdados permanecem acessíveis.
Uma class declara operações parametrizadas por um ou mais argumentos, uma instance as fornece, e a resolução encontra a instância a partir desses argumentos. Std.Associative op é indexada por uma operação, não só por um tipo.
namespace Func
class Size (α : Type) where
size : α → ℕ
instance {α : Type} :
Size (List α) where
size xs := xs.length
instance {α : Type} :
Size (Option α) where
size
| none => 0
| some _ => 1
end Func
namespace Func
def usize {α : Type} [Size α]
(a : α) : ℕ :=
Size.size a
3#eval usize [1, 2, 3]
end Func
3
O ∈ da Aula 2 e a associatividade e a comutatividade da Aula 4 são classes de tipos resolvidas por tipo.
@Membership.mem : {α : outParam (Type u_1)} → {γ : Type u_2} → [self : Membership α γ] → γ → α → Prop#check @Membership.mem
@Std.Associative : {α : Sort u_1} → (α → α → α) → Prop#check @Std.Associative
Membership dá ao ∈ o seu significado por meio de uma instância escolhida pelo tipo do contêiner, e Std.Associative e Std.Commutative são as instâncias que ac_rfl consultou.
Uma árvore é uma leaf ou uma branch com um valor e duas subárvores; as funções recorrem sobre as subárvores.
namespace Func
inductive Tree (α : Type) where
| leaf
| branch (l : Tree α) (x : α)
(r : Tree α)
def mirror {α : Type} :
Tree α → Tree α
| .leaf => .leaf
| .branch l x r =>
.branch (mirror r) x
(mirror l)
end Func
namespace Func
def treeSize {α : Type} :
Tree α → ℕ
| .leaf => 0
| .branch l _ r =>
treeSize l + 1 + treeSize r
def t1 : Tree ℕ :=
.branch (.branch .leaf 1 .leaf)
2 .leaf
2#eval treeSize t1
end Func
2O mesmo esquema constrói opções, tipos soma e tipos dependentes que carregam informação no seu próprio tipo.
namespace Func
def mapOption {α β : Type}
(f : α → β) :
Option α → Option β
| none => none
| some a => some (f a)
def fromSum : ℕ ⊕ Bool → ℕ
| .inl n => n
| .inr b => if b then 1 else 0
end Func
namespace Func
inductive Vec (α : Type) :
ℕ → Type where
| nil : Vec α 0
| cons {n : ℕ} :
α → Vec α n → Vec α (n + 1)
end Func
Um Vec α n é uma lista de comprimento n. As semanas seguintes desenvolvem os tipos dependentes.
Uma expressão é uma constante, uma variável, uma soma ou um produto, e um avaliador computa o seu valor sob um ambiente.
namespace Func
inductive AExp where
| const (i : ℤ)
| var (x : String)
| add (a b : AExp)
| mul (a b : AExp)
def eval (env : String → ℤ) :
AExp → ℤ
| .const i => i
| .var x => env x
| .add a b => eval env a + eval env b
| .mul a b => eval env a * eval env b
end Func
namespace Func
def sampleEnv : String → ℤ :=
fun s => if s = "x" then 3 else 0
def e1 : AExp :=
.add (.const 2)
(.mul (.var "x") (.const 5))
17#eval eval sampleEnv e1
end Func
17A sintaxe de uma pequena linguagem; o seu ambiente é o estado.
Mais uma instância estende Size a árvores, e uma função mede uma lista inteira de valores dimensionáveis por resolução.
namespace Func
instance {α : Type} :
Size (Tree α) where
size := treeSize
def totalSize {α : Type} [Size α] :
List α → ℕ
| [] => 0
| x :: xs =>
Size.size x + totalSize xs
end Func
namespace Func
4#eval totalSize [t1, mirror t1]
end Func
4
A resolução encontra a instância de árvore porque os elementos são árvores, o mesmo mecanismo de Membership.
Uma conta se estende a uma conta nomeada, mantendo os dois campos herdados e acrescentando um.
namespace Func
structure Account where
owner : String
balance : ℤ
structure NamedAccount extends Account where
nickname : String
def acc : NamedAccount :=
{ owner := "A", balance := 100, nickname := "main" }
example : acc.owner = "A" := rfl
end Func
O único construtor de uma estrutura é o And.intro da Aula 1, com campos nomeados em vez de posicionais.
Espelhar duas vezes devolve a árvore original. Sobre uma árvore fechada isso vale por computação.
namespace Func
example : mirror (mirror t1) = t1 := rfl
end Func
A lei geral mirror (mirror t) = t, para toda árvore, não é uma computação. Ela precisa de indução estrutural, com as chamadas recursivas de mirror fornecendo as hipóteses de indução, e é o primeiro exemplo resolvido da Aula 7.
Um tipo indutivo é os seus construtores, e o núcleo deriva um recursor, casesOn, injetividade e disjunção.
A recursão estrutural termina, e Lean admite apenas definições que terminam.
match e if são casamento de padrões como expressão, e Option empacota um resultado parcial.
Uma estrutura é um tipo indutivo de único construtor com campos nomeados, construída com ⟨…⟩ ou { … } e estendida com extends.
Uma classe de tipos é uma estrutura de operações resolvida por tipo, o mecanismo por trás do ∈ e do ac_rfl das aulas anteriores.
O mesmo esquema constrói árvores, opções, somas e vetores dependentes.
A Aula 7 deriva a indução estrutural do recursor e prova as leis enunciadas aqui.
Exercícios: veja as notas de aula.