class Preorder (α : Type u) extends LE α where
  le_refl  (a : α) : a ≤ a
  le_trans {a b c : α} : a ≤ b → b ≤ c → a ≤ c

instance {α : Type u} {β : α → Type v} [(a : α) → Preorder (β a)] : Preorder ((a : α) → β a) where
  le f g := ∀ a, f a ≤ g a
  le_refl f  := fun a => Preorder.le_refl (f a)
  le_trans   := fun h₁ h₂ a => Preorder.le_trans (h₁ a) (h₂ a)

-- In Lean 3, we defined `monotone` using the strict implicit notation `{{ ... }}`.
-- Implicit lambdas in Lean 4 allow us to use the regular `{...}`
def Monotone [Preorder α] [Preorder β] (f : α → β) :=
  ∀ {a b}, a ≤ b → f a ≤ f b

theorem monotone_id [Preorder α] : Monotone (α := α) id :=
  fun h => h

theorem monotone_id' [Preorder α] : Monotone (α := α) id :=
  @fun a b h => h -- `@` disables implicit lambdas

theorem monotone_id'' [Preorder α] : Monotone (α := α) id :=
  fun {a b} (h : a ≤ b) => h -- `{a b}` disables implicit lambdas

theorem monotone_const [Preorder α] [Preorder β] (b : β) : Monotone (fun a : α => b) :=
  fun _ => Preorder.le_refl b

theorem monotone_comp [Preorder α] [Preorder β] [Preorder γ] {g : β → γ} {f : α → β} (m_g : Monotone g) (m_f : Monotone f) : Monotone (g ∘ f) :=
  fun h => m_g (m_f h)

theorem monotone_fun [Preorder α] [Preorder γ] {f : α → β → γ} (m : (b : β) → Monotone (fun a => f a b)) : Monotone f :=
  fun h b => m b h

theorem ex [Preorder α] {f g h : α → α} (m_h : Monotone h) (m_g : Monotone g) (m_f : Monotone f) : Monotone (h ∘ g ∘ f) :=
  monotone_comp m_h (monotone_comp m_g m_f) -- implicit lambdas in action here
