Intro Lean Functors and Structures/Product Types
Posted on August 2, 2019
Tags: lean
1 Simp
@[simp] theorem list.is_prefix_refl (l : list α) : l <+: l :=
⟨[], by simp⟩
- The decorator looking like
@[simp]
is actually injecting the theorem into the simp method.- This makes simp more powerful
- Lean calls this an attribute
1.1 Simp *
lemma
begin*]
simp [ end
- the above simp uses ALL hypothesis in the local context.
2 Understanding inductive types
- One can think of terms formed from recursively inductive types as the ROOT of a tree.
/
\ -------- (cons rule)
/
\ x::a y::nlist
/
\ -------- (cons rule)
/
\ y :: nlist cons x
- Suppose we had an inductive type with no base case
- it would be non-terminating
: Type
inductive nlist| cons : a -> nlist -> nlist
cons
/ \
5 cons
/ \
7 ..inf..
Takeaway: We Must have a NON-RECURSIVE base constructor
Heuristic: the recursive type must only appear as the last argument to be considered a non-recursive constructor
good1 :: a -> b -> c -> nlist
good2 :: nlist
good3 :: a -> nlist
bad1 :: nlist -> nlist -> a -> nlist
bad2 :: nlist -> nlist
3 Induction and function definition
take {α : Type} : ℕ → list α → list α
def | 0 _ := [] --A,B
| (n + 1) [] := [] --C
| (n + 1) (x :: xs) := x :: take n xs --D
Below is an inductive proof with leafs labeled as A,B,C,D.
A B C D
ℕ=0, ℕ=0 ℕ=k-1 ℕ=k-1
list=[] list=xs list=[] list=xs
\ / \ /
\ / \ /
ℕ=0,∀list ℕ=k-1,∀list
\ /
\ /
∀ℕ ∀list
Proof
=n-1
ℕ=[]
list
corresponds to Code
-- LHS => RHS + IH
| (k + 1) [] := []
=n-1,list=[] inductive hypothesis is k,[] which corresponds to ℕ
Takeaway:
- The definition of any function is just an inductive proof.
4 typeclasses
class myFunctor
fmap
define the interface with virtual methoddef fmapList
define the concrete functioninstance:myFunctor List
implementList
asmyFunctor
by binding concrete functionfmapList
to virtual methodfmap
---------------------------------------------------
/-
AKA methods will adapt to the type of the parameter.
typeclasses basically interfaces(which give polymorphism) The set of allowable types is the Typeclass.
.
egfmap is the polymorphic method
of allowable types is {List,Maybe,Tree}
set This above set is a Typeclass called myFunctor
`[myFunctor List]` means "List ∈ myFunctor"
-/
/-
In lean typeclasses were annotated structures/records.
Typeclasses are like interface classes in OO containing abstract methods
Classes that implement interfaces must also have
class
virtual polymorphic methods to be implement the abstract
-/
--1. typeclass which is similar to interface
class myFunctor (f: Type -> Type) :=
fmap: (a-> b) -> (f a -> f b) --virtual method that needs to be implemented
--2. concrete method
: (a -> b) -> (List a -> List b) :=
def fmapList=> λ mylist => match mylist with
λ f | [] => []
| x::xs => [f x] ++ (fmapList f xs)
--3. List implements the myFunctor interface
instance : myFunctor List where
fmap := fmapList --bind concrete method to virtual method
: Nat -> Nat := λ x => x + 2
def add2
#reduce myFunctor.fmap add2 [1,3,5]
Maybe (T : Type) :=
inductive | Just : T -> Maybe T
| Nil : Maybe T
Maybe
open
: (a -> b) -> (Maybe a -> Maybe b) :=
def fmapMaybe=> λ mymaybe => match mymaybe with
λ f | Nil => Nil
| Just (x:a) => Just (f x)
--`instance : myFunctor Maybe where` is an alternative syntax
instance : myFunctor Maybe :=
{fmap := fmapMaybe
}
#reduce myFunctor.fmap add2 (Just 3)
#reduce myFunctor.fmap add2 Nil
5 structures
- Remember structures are products types, meaning all structures have a projection function implicity.
Point (T : Type) where
structure : T
x : T
y
#check Point
--Point : Type → Type
#check @Point.mk
--@Point.mk : {T : Type} → T → T → Point T
#reduce Point.mk 4 5
--{ x := 4, y := 5 }
#check @Point.x
--@Point.x : {T : Type} → Point T → T
/- Take notice of how structure Point gives us for free
Term Introduction function: Point.mk
Product Projection function: Point.x
-/
5.1 Typeclasses as structures
5.1.1 Example 1
/-`@[class]` annotated structure is a typeclass in haskell
Functor,Monad,Applicative are examples.
In regular langs, they are almost like interfaces.
-/
@[class] structure Myinhabited (a : Type) : Type :=
: a )
(default
@[instance] def Point.Myinhabited : Myinhabited Bool :=
:= true}
{default
#reduce Point.Myinhabited
/-`@[instance]` is how you use a typeclass.
In regular langs, this is like implementing an interface.
-/
5.1.2 Example 2
: (a -> b) -> (List a -> List b) :=
def fmapListHelper=> λ la => match la with
λ f | [] => []
| x::xs => [f x] ++ (fmapListHelper f xs)
: Nat -> Nat := λ x => x + 2
def add2
/- EXAMPLE 2: Functor -/
@[class] structure MyFunctor (f: Type -> Type) where
fmap : (a-> b) -> (f a -> f b))
(
@[instance] def List.MyFunctor : MyFunctor List :=
fmap := λ f => λ la => match la with
{| [] => []
| x::xs => [f x] ++ (fmapListHelper f xs) }
#reduce List.MyFunctor.fmap add2 [1,3,5]
#check MyFunctor
: (a -> b) -> (List a -> List b) :=
def fmapListHelper=> λ la => match la with
λ f | [] => []
| x::xs => [f x] ++ (fmapListHelper f xs)
: Nat -> Nat := λ x => x + 2
def add2 /- EXAMPLE 2: Functor w/ typical notation-/
class MyFunctor2 (f: Type -> Type) :=
fmap: (a-> b) -> (f a -> f b)
instance : MyFunctor2 List where
fmap := λ f => λ la => match la with
| [] => []
| x::xs => [f x] ++ (fmapListHelper f xs)
#reduce MyFunctor2.fmap add2 [1,3,5]
#check MyFunctor2
6 Lean emulate OOP class
Lean’s inductive data types can be used to emulate OOP classes.
namespace mytree
universe u
Tree (T: Type u)
inductive | leaf : Tree T
| node (left: Tree T) (key : Nat) (value: T) (right: Tree T) : Tree T
deriving Repr
--Observe 2 different ways to define contains
--1st way: we bind the contains method to Tree datatype,
--behaves like a class method or a pointer receiver in golang
--or the `struct` `impl` pattern in Rust
: Tree T) (k : Nat) : Bool :=
def Tree.contains (t
match t with | leaf => false
| node left key value right =>
if k < key then
.contains k --notice using dot operator, 1st argument is omitted
leftelse if key < k then
.contains k
rightelse
true
--2nd way: a normal function
: Tree T) (k : Nat) : Bool :=
def contains (t
match t with | Tree.leaf => false
| Tree.node left key value right =>
if k < key then
contains left kelse if key < k then
contains right kelse
true
end mytree