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)
\ /
cons x y :: nlist- Suppose we had an inductive type with no base case
- it would be non-terminating
inductive nlist: Type
| 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 -> nlist3 Induction and function definition
def take {α : Type} : ℕ → list α → list α
| 0 _ := [] --A,B
| (n + 1) [] := [] --C
| (n + 1) (x :: xs) := x :: take n xs --DBelow 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) [] := []
inductive hypothesis is k,[] which corresponds to ℕ=n-1,list=[]Takeaway:
- The definition of any function is just an inductive proof.
4 typeclasses
class myFunctorfmapdefine the interface with virtual methoddef fmapListdefine the concrete functioninstance:myFunctor ListimplementListasmyFunctorby binding concrete functionfmapListto virtual methodfmap
---------------------------------------------------
/-
typeclasses basically interfaces(which give polymorphism) AKA methods will adapt to the type of the parameter.
The set of allowable types is the Typeclass.
eg.
fmap is the polymorphic method
set of allowable types is {List,Maybe,Tree}
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
virtual polymorphic methods to be implement the abstract class
-/
--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
def fmapList: (a -> b) -> (List a -> List b) :=
λ f => λ mylist => match mylist with
| [] => []
| 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
def add2 : Nat -> Nat := λ x => x + 2
#reduce myFunctor.fmap add2 [1,3,5]
inductive Maybe (T : Type) :=
| Just : T -> Maybe T
| Nil : Maybe T
open Maybe
def fmapMaybe: (a -> b) -> (Maybe a -> Maybe b) :=
λ f => λ mymaybe => match mymaybe with
| 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 Nil5 structures
- Remember structures are products types, meaning all structures have a projection function implicity.
structure Point (T : Type) where
x : T
y : T
#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 :=
(default : a )
@[instance] def Point.Myinhabited : Myinhabited Bool :=
{default := true}
#reduce Point.Myinhabited
/-`@[instance]` is how you use a typeclass.
In regular langs, this is like implementing an interface.
-/5.1.2 Example 2
def fmapListHelper: (a -> b) -> (List a -> List b) :=
λ f => λ la => match la with
| [] => []
| x::xs => [f x] ++ (fmapListHelper f xs)
def add2 : Nat -> Nat := λ x => x + 2
/- 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 MyFunctordef fmapListHelper: (a -> b) -> (List a -> List b) :=
λ f => λ la => match la with
| [] => []
| x::xs => [f x] ++ (fmapListHelper f xs)
def add2 : Nat -> Nat := λ x => x + 2
/- 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
inductive Tree (T: Type u)
| 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
def Tree.contains (t: Tree T) (k : Nat) : Bool :=
match t with
| leaf => false
| node left key value right =>
if k < key then
left.contains k --notice using dot operator, 1st argument is omitted
else if key < k then
right.contains k
else
true
--2nd way: a normal function
def contains (t: Tree T) (k : Nat) : Bool :=
match t with
| Tree.leaf => false
| Tree.node left key value right =>
if k < key then
contains left k
else if key < k then
contains right k
else
true
end mytree