import Mathlib -- Review type classes (very different from classes in OOP!) -- Warm fuzzy things namespace Monad #eval [1, 2, 3].map (· * 2) #eval (· * 2) <$> [1, 2, 3] #eval (some 2).map (· * 2) #eval none.map (· * 2) #check List.map #check Option.map #check Except.map #check Tree.map -- #check .map class Functor' (f : Type → Type) where map : (α → β) → f α → f β scoped infixr:100 " <$> " => Functor'.map scoped infixr:100 " 🤑 " => Functor.map #eval (· * 2) 🤑 [1, 2, 3] class LawfulFunctor' f [Functor' f] where id_map (x : f α) : id <$> x = x comp_map (g : α → β) (h : β → γ) (x : f α) : (h ∘ g) <$> x = h <$> (g <$> x) @[simp] instance : Functor' Option where map f | some x => some (f x) | none => none instance : LawfulFunctor' Option where id_map x := by cases x <;> simp comp_map g h x := by cases x <;> simp #simp (some 3).map (· * ·) class Applicative' f extends Functor' f where pure : α → f α seq : f (α → β) → f α → f β export Applicative' (pure) scoped infixl:60 " <*> " => Applicative'.seq class LawfulApplicative' f [Applicative' f] extends LawfulFunctor' f where pure_seq (g : α → β) (x : f α) : pure g <*> x = g <$> x map_pure (g : α → β) (x : α) : g <$> (pure x : f α) = pure (g x) seq_pure (g : f (α → β)) (x : α) : g <*> pure x = (· x) <$> g seq_assoc (x : f α) (g : f (α → β)) (h : f (β → γ)) : h <*> (g <*> x) = (· ∘ ·) <$> h <*> g <*> x comp_map g h x := (by repeat rw [← pure_seq] simp [seq_assoc, map_pure, seq_pure]) #eval ((· * ·) <$> (Except.ok 3) <*> (Except.ok 4) : Except String ℕ) def one_over (x : ℕ) : Option ℚ := if x = 0 then none -- Division by 0 is undefined else some <| 1 / x #eval one_over 42 #eval one_over (some 2) #eval (some 2) >>= one_over class Monad' m extends Applicative' m where bind : m α → (α → m β) → m β map f x := bind x (pure ∘ f) seq f x := bind f (· <$> x) scoped infixl:55 " >>= " => Monad'.bind class LawfulMonad' m [Monad' m] extends LawfulApplicative' m where bind_pure_comp (f : α → β) (x : m α) : x >>= (fun a ↦ pure (f a)) = f <$> x bind_map (f : m (α → β)) (x : m α) : f >>= (· <$> x) = f <*> x pure_bind (x : α) (f : α → m β) : pure x >>= f = f x bind_assoc (x : m α) (f : α → m β) (g : β → m γ) : x >>= f >>= g = x >>= fun y ↦ f y >>= g map_pure g x := (by rw [← bind_pure_comp, pure_bind]) seq_pure g x := (by simp [← bind_map, map_pure, bind_pure_comp]) seq_assoc x g h := (by simp [← bind_pure_comp, ← bind_map, bind_assoc, pure_bind]) @[simp] instance : Monad' Option where pure x := .some x bind x f := match x with | some x => f x | none => none instance : LawfulMonad' Option where id_map := by simp grind pure_seq := by simp bind_pure_comp := by simp bind_map := by simp pure_bind := by simp bind_assoc := by simp grind @[simp] instance : Monad' List where pure x := [x] bind xs f := xs.map f |>.flatten instance : LawfulMonad' List where id_map xs := by simp induction xs <;> grind pure_seq := by simp bind_pure_comp f xs := by simp induction xs <;> grind bind_map := by simp pure_bind := by simp bind_assoc xs := by simp induction xs <;> grind end Monad def option_div (x_wrapped : Option ℕ) (y_wrapped : Option ℕ) : Option ℚ := y_wrapped >>= fun y ↦ if y = 0 then none else x_wrapped >>= fun x ↦ some <| x / y #eval option_div (some 3) (some 0) def option_div' (x_wrapped : Option ℕ) (y_wrapped : Option ℕ) : Option ℚ := do let x ← x_wrapped let y ← y_wrapped if y = 0 then none else some <| x / y -- https://slightknack.dev/blog/do-notation/ -- See Main.lean def ICan'tBelieveItCanSort [LinearOrder α] (A : Array α) := Id.run do let N := A.size let mut A := A.toVector for hi : i in [:N] do for hj : j in [:N] do if A[i] < A[j] then A := A.swap i j return A.toArray def Array.insSort [LinearOrder α] (A : Array α) := Id.run do let N := A.size let mut A := A.toVector for hi : i in [:N] do for hj : j in [:i] do have := Membership.get_elem_helper hi rfl if A[i - j] < A[i - j - 1] then A := A.swap (i - j - 1) (i - j) else break return A.toArray -- Local imperativity https://dl.acm.org/doi/10.1145/3547640 def kadane (A : Array ℤ) := Id.run do let mut cur := 0 let mut ans := 0 for x in A do cur := max x (cur + x) ans := max ans cur return ans def UpToN (xs : List ℕ) : List ℕ := do let x ← xs let y ← List.range x return y #eval UpToN [1, 2, 3] namespace Imperative open Std.Do variable [LinearOrder α] (A : Array α) theorem insSortPerm : A.insSort.Perm A := by generalize h : A.insSort = x apply Id.of_wp_run_eq h mvcgen invariants · ⇓⟨_, A'⟩ => ⌜A.Perm A'.toArray⌝ · ⇓⟨_, A'⟩ => ⌜A.Perm A'.toArray⌝ with grind [Array.Perm.trans, Array.Perm.symm, Array.swap_perm] abbrev Sorted := ∀ i (_ : 0 ≤ i ∧ i < A.size - 1), A[i] ≤ A[i + 1] abbrev SortedRange l r (_ : l ≤ A.size := by grind) (_ : r ≤ A.size := by grind) := ∀ i (_ : l ≤ i ∧ i < r - 1), A[i] ≤ A[i + 1] theorem insSortSorted : Sorted A.insSort := by generalize h : A.insSort = x apply Id.of_wp_run_eq h mvcgen <;> expose_names · exact ⇓⟨xs, A'⟩ => ⌜SortedRange A'.toArray 0 xs.pos (by grind) (by grind [List.length_append, xs.property])⌝ · exact ⇓⟨xs, A'⟩ => ⌜SortedRange A'.toArray 0 (cur - xs.pos) ∧ SortedRange A'.toArray (cur - xs.pos) (cur + 1) ∧ ((_ : 0 < xs.pos ∧ xs.pos < cur) → A'[cur - xs.pos - 1]'(by grind) ≤ A'[cur - xs.pos + 1]'(by grind))⌝ case vc1.step.isTrue => simp at h_5 ⊢ and_intros · grind · intro i hi by_cases i = cur - cur_1 - 1 ∨ i = cur - cur_1 · grind · grind [h_5.2.1 i (by grind)] · intro _ grind [h_5.1 (cur - cur_1 - 2) (by grind)] case vc2.step.isFalse => simp_all and_intros · grind · intro i hi by_cases i < cur - cur_1 - 1 · exact h_5.1 i (by grind) · by_cases cur - cur_1 ≤ i · exact h_5.2.1 i (by grind) · grind · grind case vc4.step.post.success => simp at h_3 ⊢ grind all_goals grind theorem insSortCorrect : A.insSort.Perm A ∧ Sorted A.insSort := ⟨insSortPerm A, insSortSorted A⟩ end Imperative