module -- Since we do not have ENNReal in core, we just axiomatize it all for this test opaque ENNReal : Type axiom ENNReal.sup : ∀ {α}, (α → ENNReal) → ENNReal axiom ENNReal.sum : ∀ {α}, (α → ENNReal) → ENNReal axiom ENNReal.add : ENNReal → ENNReal → ENNReal axiom ENNReal.mul : ENNReal → ENNReal → ENNReal noncomputable instance : Add ENNReal where add := .add noncomputable instance : Mul ENNReal where mul := .mul @[instance] axiom ENNReal.zero : Zero ENNReal axiom ENNReal.one : ENNReal axiom ENNReal.one_half : ENNReal @[simp] axiom ENNReal.mul_one : ∀ x, x * ENNReal.one = x @[simp] axiom ENNReal.mul_zero : ∀ (x : ENNReal), x * 0 = 0 @[simp] axiom ENNReal.add_zero : ∀ (x : ENNReal), x + 0 = x @[simp] axiom ENNReal.zero_add : ∀ (x : ENNReal), 0 + x = x @[simp] axiom ENNReal.sum_bool : ∀ f, sum f = f true + f false @[simp] axiom ENNReal.sum_const_zero : ∀ α, ENNReal.sum (fun (_ : α) => 0) = 0 @[simp] axiom ENNReal.sum_dirac : ∀ α [DecidableEq α] (f : α → ENNReal) y, ENNReal.sum (fun x => if x = y then f x else 0) = f y @[instance] axiom ENNReal.le : LE ENNReal axiom ENNReal.le_refl : ∀ (x : ENNReal), x ≤ x axiom ENNReal.le_trans : ∀ {x y z: ENNReal}, x ≤ y → y ≤ z → x ≤ z axiom ENNReal.le_antisymm : ∀ {x y : ENNReal}, x ≤ y → y ≤ x → x = y section set_option linter.unusedVariables false axiom ENNReal.sum_mono : ∀ {α} (s₁ s₂ : α → ENNReal) (h : ∀ x, s₁ x ≤ s₂ x), ENNReal.sum s₁ ≤ ENNReal.sum s₂ axiom ENNReal.sup_mono : ∀ {α} (s₁ s₂ : α → ENNReal) (h : ∀ x, s₁ x ≤ s₂ x), ENNReal.sup s₁ ≤ ENNReal.sup s₂ axiom ENNReal.mul_mono : ∀ (a b c Distr : ENNReal) (h₁ : a ≤ c) (h₂ : b ≤ Distr), a * b ≤ c * Distr axiom ENNReal.le_sup : ∀ {α} (a : ENNReal) (s : α → ENNReal) (i : α) (h : a ≤ s i), a ≤ ENNReal.sup s axiom ENNReal.sup_le : ∀ {α} (a : ENNReal) (s : α → ENNReal) (h : ∀ (i : α), s i ≤ a), ENNReal.sup s ≤ a end /-- Distributions (not normalized, which is curcial, else we don't have ⊥.) -/ def Distr (α : Type) : Type := α → ENNReal noncomputable def Distr.join : Distr (Distr α) → Distr α := fun dd x => ENNReal.sum (fun Distr => Distr x * dd Distr ) noncomputable instance : Functor Distr where map f Distr := fun x => ENNReal.sum (fun y => open Classical in if f y = x then Distr y else 0) noncomputable instance : Pure Distr where pure x := fun y => open Classical in if x = y then .one else 0 noncomputable instance : Bind Distr where bind Distr f := fun x => ENNReal.sum (fun y => Distr y * f y x) open Lean.Order noncomputable instance : PartialOrder (Distr α) where rel d1 d2 := ∀ x, d1 x ≤ d2 x rel_refl _ := ENNReal.le_refl _ rel_trans h1 h2 _ := ENNReal.le_trans (h1 _) (h2 _) rel_antisymm h1 h2 := funext (fun _ => ENNReal.le_antisymm (h1 _) (h2 _)) noncomputable instance : CCPO (Distr α) where has_csup := by intro c hchain exists fun x => ENNReal.sup fun (Distr : Subtype c) => Distr.val x intro d₁ constructor next => intro h d₂ hd₂ x apply ENNReal.le_trans ?_ (h x) apply ENNReal.le_sup (i := ⟨d₂, hd₂⟩) apply ENNReal.le_refl next => intro h x apply ENNReal.sup_le intro Distr apply h Distr.1 Distr.2 x noncomputable instance : MonoBind Distr where bind_mono_left := by intro α β d₁ d₂ f h₁₂ y unfold bind instBindDistr dsimp apply ENNReal.sum_mono intro x apply ENNReal.mul_mono · apply h₁₂ · apply ENNReal.le_refl bind_mono_right := by intro α β Distr f₁ f₂ h₁₂ y apply ENNReal.sum_mono intro x apply ENNReal.mul_mono · apply ENNReal.le_refl · apply h₁₂ noncomputable def coin : Distr Bool := fun _ => .one_half noncomputable def geom : Distr Nat := do let head ← coin if head then return 0 else let n ← geom return (n + 1) partial_fixpoint /-- info: geom.eq_1 : geom = do let head ← coin if head = true then pure 0 else do let n ← geom pure (n + 1) -/ #guard_msgs in #check geom.eq_1 -- And we can can do proofs about this theorem geom_0 : geom 0 = .one_half := by rw [geom]; simp [bind, coin, pure] theorem geom_succ : geom (n+1) = .one_half * geom n := by conv => lhs; rw [geom] simp [bind, coin, pure, apply_ite]