Lecture 3: Side effects, type classes, and monads
Jesper Cockx
2 September 2019
A type class offers one or more functions for a generic type. Examples:
Print A:
print : A → StringMonoid M type class:
∅ : M_+_ : M → M → MEq A:
_==_ : A → A → Bool and/or _≟_ : (x y : A) -> Dec (x ≡ y)Functor F:
fmap : {A B : Set} → F A → F BWhy not have print : {A : Set} -> A -> String?
Because of parametricity, print would have to be a constant function.
Type classes allow a different implementation at each type!
A type class is implemented by using a record type + instance arguments
Instance arguments are Agda’s builtin mechanism for ad-hoc overloading (~ type classes in Haskell).
Syntax:
{{x : A}} → ...instance ...When using a function of type {{x : A}} → B, Agda will automatically resolve the argument if there is a unique instance of the right type in scope.
record Print {ℓ} (A : Set ℓ) : Set ℓ where field print : A → String open Print {{...}} -- print : {{r : Print A}} → A → String instance PrintBool : Print Bool print {{PrintBool}} true = "true" print {{PrintBool}} false = "false" PrintString : Print String print {{PrintString}} x = x testPrint : String testPrint = (print true) ++ (print "a string")
“To be is to do” –Socrates
“To do is to be” –Sartre
“Do be do be do” –Sinatra.
Agda is a pure language: functions have no side effects
But a typechecker has many side effects:
Monad is a typeclass with two fields return and _>>=_.
M A ~ “computations that may have some side-effects (depending on M) and return an A”
Examples: Maybe, Reader, Error, State, …
do notationdo is syntactic sugar for repeated binds: instead of
_ : Maybe ℤ _ = (just (-[1+ 3 ])) >>= λ x → (just (+ 5) ) >>= λ y → return (x + y)you can write:
_ : Maybe ℤ _ = do x ← just (-[1+ 3 ]) y ← just (+ 5) return (x + y)
doThere can be a pattern to the left of a ←, alternative cases can be handled in a local where
pred : ℕ → Maybe ℕ pred n = do suc m ← just n where zero → nothing return m
dopostulate test : (m n : ℕ) → Maybe (m ≡ n) cast : (m n : ℕ) → Vec ℤ m → Maybe (Vec ℤ n) cast m n xs = do refl ← test m n return xs
Pattern matching allows typechecker to learn new facts!
See V1/TypeChecker.agda.
Exercise: extend the typechecker with rules for the new syntactic constructions you added before
For type-checking variables, we need the following side-effects:
How to ensure statically that each variable in scope has a name?
All typeP : A → Set and xs : List A, All P xs associates an element of P x to each x ∈ xs:
data All {A : Set} (P : A → Set) : List A → Set where [] : All P [] _∷_ : ∀ {x xs} → P x → All P xs → All P (x ∷ xs)
Env Γ = All Val ΓTCCxt Γ = All (\_ → Name) ΓWe need to modify both Γ : Cxt and ρ : TCCxt Γ!
Possible solutions:
Σ Cxt TCCxt?An indexed monad = a monad with two extra parameters for the (static) input and output states
record IMonad {I : Set} (M : I → I → Set → Set) : Set₁ where field return : ∀ {A i} → A → M i i A _>>=_ : ∀ {A B i j k} → M i j A → (A → M j k B) → M i k B
Examples:
TCDecl monad (see V2/TypeChecker.agda).Exec monad (see V3/Interpreter.agda).We now have a correct-by-construction typechecker + interpreter.
However, some things are still missing:
Goal. Use Haskell library for generating these automatically
Import a Haskell module:
{-# FOREIGN GHC import HaskellModule.hs #-}
Bind Haskell function to Agda name:
postulate agdaName : AgdaType {-# COMPILE GHC agdaName = haskellCode #-}
Bind Haskell datatype to Agda datatype:
data D : Set where c₁ c₂ : D {-# COMPILE GHC D = data hsData (hsCon₁ | hsCon₂) #-}
-- In file `AST.agda`: {-# FOREIGN GHC import While.Abs #-} data Type : Set where bool int : Type {-# COMPILE GHC Type = data Type ( TBool | TInt ) #-}
BNFC is a Haskell library for generating Haskell code from a grammar:
See While.cf for the grammar of WHILE.
Extend the typechecker and the BNFC grammar with the new syntactic constructions you added.
Don’t forget to update the Haskell bindings in AST.agda!
Running the test suite:
 make parser: compile the parser and run it on /test/gcd.c. - make test: compile the whole typechecker and run it on the examples in /test