Zulip Chat Archive
Stream: new members
Topic: Partial functions using `option`
Rémi Bottinelli (Mar 04 2022 at 07:18):
Hey! I'm reading #tpil, in which they propose as an exercise to define partial functions using the option
type, and to verify that composition behaves as expected.
Here is what I have:
inductive op (α : Sort u)
| so (a : α) : op
| no {} : op
example {α : Sort u} (x : op α) : (x = op.no) ↔ ¬ (∃ a : α, x = op.so a) :=
⟨ λ xisno ⟨a,xissoa⟩, op.no_confusion (xissoa.symm.trans xisno)
, λ noexists,
@op.rec
α
(λ (x : op α), ¬ (∃ a : α, x = op.so a) → x = op.no )
( λ (b : α) noexists, absurd (⟨b,rfl⟩ : ∃ a, op.so b = op.so a) noexists )
( λ noexists, rfl)
x
noexists
⟩
def comp {α β γ : Sort u} (f : α → op β) (g : β → op γ) : α → op γ :=
λ x : α, op.rec (λ b, g b) (op.no) (f x)
lemma comp_part {α β γ : Sort u} (f : α → op β) (g : β → op γ) :
∀ x, (comp f g x) = op.no ↔
( f x = op.no ∨
( ∃ y : β, f x = op.so y ∧ g y = op.no)) := sorry
but trying to prove comp_part
, I get stuck after the basic split/intro
boilerplate. I tried defining intermediary lemmas but it doesn't seem to be any use. I also looked at the documentation to see how it's implemented in practice, but it seems this kind of "partial function type" is more of a toy example, and partial functions are implemented differently in practice (using predicates and subtypes). I'd appreciate some hints on how to proceed!
Kyle Miller (Mar 04 2022 at 08:15):
You can use the raw recursor if you want, but match
syntax is pretty nice. For the sorry
, it comes down to doing cases on f x
. To do this effectively, you need to have an equality that lets you relate f x
to the possible cases, and tactic#generalizes' is one solution to this.
import tactic.basic
universe u
inductive op (α : Sort u)
| so (a : α) : op
| no {} : op
example {α : Sort u} (x : op α) : (x = op.no) ↔ ¬ (∃ a : α, x = op.so a) :=
by { cases x; simp }
def comp {α β γ : Sort u} (f : α → op β) (g : β → op γ) : α → op γ :=
λ x, match f x with
| (op.so b) := g b
| op.no := op.no
end
lemma comp_part {α β γ : Sort u} (f : α → op β) (g : β → op γ) (x : α) :
comp f g x = op.no ↔ f x = op.no ∨ ∃ y : β, f x = op.so y ∧ g y = op.no :=
begin
generalize' h : f x = y,
cases y; simp [comp, h],
end
Kyle Miller (Mar 04 2022 at 08:17):
Here's how you can do that without generalize'
:
lemma comp_part {α β γ : Sort u} (f : α → op β) (g : β → op γ) (x : α) :
comp f g x = op.no ↔ f x = op.no ∨ ∃ y : β, f x = op.so y ∧ g y = op.no :=
begin
have : ∃ y, f x = y := ⟨f x, rfl⟩,
cases this with y h,
cases y; simp [comp, h],
end
Kevin Buzzard (Mar 04 2022 at 08:20):
Here's the API more generally:
import tactic
universe u
inductive op (α : Sort u)
| so (a : α) : op
| no {} : op
example {α : Sort u} : ∀ (x : op α), (x = op.no) ↔ ¬ (∃ a : α, x = op.so a)
| (op.so a) := by simp
| op.no := by simp
def comp {α β γ : Sort u} (f : α → op β) (g : β → op γ) : α → op γ :=
λ x : α, op.rec (λ b, g b) (op.no) (f x)
variables {α β γ : Sort u} (f : α → op β) (g : β → op γ)
lemma comp_no (a : α) (h : f a = op.no) : comp f g a = op.no :=
begin
unfold comp,
cases f a,
{ cases h },
{ refl },
end
lemma comp_so (a : α) (b : β) (h : f a = op.so b) : comp f g a = g b :=
begin
unfold comp,
cases f a,
{ cases h, refl },
{ cases h },
end
Kyle Miller (Mar 04 2022 at 08:22):
Regarding partial function types: docs#option is the partial value where you can computably tell whether a value is present, and docs#part is the one where you can't (you need to be able to offer a proof of whatever dom
might be), though the value is computable if you're able to prove it's present.
Kevin Buzzard (Mar 04 2022 at 08:25):
I feel that questions like this are very much on the "type theory" side of Lean and whilst I find them quite interesting as little puzzles, I'm not sure they're too useful for mathematicians (I don't teach anything like this in my course). Getting the basic API out (comp_no
and comp_so
) from comp
is just a case of knowing what the rules of refl
are in some sense; the point is that the output of a recursor applied to a constructor is what the corresponding recursor input is, and the proof is refl. I remember spending some time starting at the relevant page of the reference manual when I was getting my head around all of this.
Kyle Miller (Mar 04 2022 at 08:26):
I'm not sure if there's anything like it in mathlib, but then there's also a fully noncomputable version of partial values:
structure nc_option (α : Type*) :=
(p : α → Prop)
(subsingleton : ∀ x y, p x → p y → x = y)
This is (noncomputably) equivalent to option
, and not only can you not tell whether the value is present, but you can't tell what the value even is.
Kyle Miller (Mar 04 2022 at 08:36):
This composition, by the way, is the Kleisli arrow (known as docs#fish) for option
. It seems like there's not really that much about it in mathlib, but at least it's there.
import data.option.basic
import tactic.basic
universe u
example {α : Type u} (x : option α) : (x = none) ↔ ¬ (∃ a : α, x = some a) :=
by { cases x; simp }
def comp {α β γ : Type u} (f : α → option β) (g : β → option γ) : α → option γ :=
f >=> g
lemma comp_some {α β γ : Type u} {f : α → option β} {g : β → option γ} {x : α} {y : β}
(h : f x = some y) : comp f g x = g y :=
by simp [comp, fish, h]
lemma comp_none {α β γ : Type u} {f : α → option β} {g : β → option γ} {x : α}
(h : f x = none) : comp f g x = none :=
by simp [comp, fish, h]
lemma comp_part {α β γ : Type u} {f : α → option β} {g : β → option γ} {x : α} :
comp f g x = none ↔ f x = none ∨ ∃ y : β, f x = some y ∧ g y = none :=
begin
generalize' h : f x = y,
cases y,
{ rw [comp_none h],
simp, },
{ rw [comp_some h],
simp, },
end
Yaël Dillies (Mar 04 2022 at 09:11):
You might want to have a look at docs#PartialFun_equiv_Pointed which states that partial functions modelled with pfun
and modelled with option
are noncomputably the same.
Rémi Bottinelli (Mar 04 2022 at 09:22):
Woah, thanks all :) I don't know why, but I always trip on this "inspect-on-equality" style of functions.
Rémi Bottinelli (Mar 04 2022 at 09:58):
I went with:
lemma comp_part {α β γ : Sort u} (f : α → op β) (g : β → op γ) :
∀ x, (comp f g x) = op.no ↔ ( f x = op.no ∨ ( ∃ y : β, f x = op.so y ∧ g y = op.no)) :=
begin
intro x,
cases h : f x;
{unfold comp, simp, rw h,},
end
Rémi Bottinelli (Mar 04 2022 at 10:02):
And to be honest, this for me is a clear example of how much tactic style simplifies writing proofs. Inspector-style/recursion becomes very confusing quite quickly, but via tactics, it's very much a "game" you play with lean.
Last updated: Dec 20 2023 at 11:08 UTC