Download - Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

Transcript
Page 1: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

Dependently Typed Functional Programming withIdris

Lecture 2: Embedded Domain Specific Languages

Edwin BradyUniversity of St Andrews

[email protected]

@edwinbrady

Page 2: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

DSLs in Idris

Idris aims to support the implementation of verified domainspecific languages. To illustrate this, we begin with an interpreterfor the simply typed λ-calculus.

Page 3: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

First Attempt

Expressions

data Expr = Val Int

| Add Expr Expr

Evaluator

interp : Expr -> Int

interp (Val x) = x

interp (Add l r) = interp l + interp r

Page 4: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

First Attempt

Expressions

data Expr = Val Int

| Add Expr Expr

Evaluator

interp : Expr -> Int

interp (Val x) = x

interp (Add l r) = interp l + interp r

Page 5: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

First Attempt

Expressions

data Expr = Val Int

| Var String

| Add Expr Expr

Evaluator

interp : List (String, Int) -> Expr -> Maybe Int

interp env (Val x) = x

interp env (Var n) = case lookup n env of

Nothing => Nothing

Just val => val

interp env (Add l r) = interp env l + interp env r

Page 6: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

First Attempt

Expressions

data Expr = Val Int

| Var String

| Add Expr Expr

Evaluator

interp : List (String, Int) -> Expr -> Maybe Int

interp env (Val x) = x

interp env (Var n) = case lookup n env of

Nothing => Nothing

Just val => val

interp env (Add l r) = interp env l + interp env r

Page 7: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

Preliminaries

Types

data Ty = TyInt

| TyBool

| TyFun Ty Ty

Interpreting types

interpTy : Ty -> Type

interpTy TyInt = Int

interpTy TyBool = Bool

interpTy (TyFun s t) = interpTy s -> interpTy t

Page 8: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

Preliminaries

Types

data Ty = TyInt

| TyBool

| TyFun Ty Ty

Interpreting types

interpTy : Ty -> Type

interpTy TyInt = Int

interpTy TyBool = Bool

interpTy (TyFun s t) = interpTy s -> interpTy t

Page 9: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

Preliminaries

The Finite Sets

data Fin : Nat -> Type where

fO : Fin (S k)

fS : Fin k -> Fin (S k)

Example: Bounds Safe Vector Lookup

total

index : Fin n -> Vect a n -> a

index fO (x::xs) = x

index (fS k) (x::xs) = index k xs

Page 10: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

Preliminaries

The Finite Sets

data Fin : Nat -> Type where

fO : Fin (S k)

fS : Fin k -> Fin (S k)

Example: Bounds Safe Vector Lookup

total

index : Fin n -> Vect a n -> a

index fO (x::xs) = x

index (fS k) (x::xs) = index k xs

Page 11: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

Preliminaries

We can represent variables as a de Bruijn index

Nameless

A number, counting binders since the variable was bound

λxy .x + y =⇒ λxy .1 + 0

If there are n variables:

Fin n represents a bounded de Bruijn indexindex i G is a bounds safe lookup of variable i in context G.

Page 12: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

Preliminaries

We can represent variables as a de Bruijn index

Nameless

A number, counting binders since the variable was bound

λxy .x + y =⇒ λxy .1 + 0

If there are n variables:

Fin n represents a bounded de Bruijn indexindex i G is a bounds safe lookup of variable i in context G.

Page 13: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

Preliminaries

Environments

using (G : Vect Ty n)

data Env : Vect Ty n -> Type where

Nil : Env Nil

(::) : interpTy a -> Env G -> Env (a :: G)

data HasType : (i : Fin n) -> Vect Ty n -> Ty ->

Type where

stop : HasType fO (t :: G) t

pop : HasType k G t ->

HasType (fS k) (u :: G) t

Page 14: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

Preliminaries

Environment Example

ctxt : Vect Ty (S (S O))

ctxt = [TyInt, TyBool]

env : Env ctxt

env = [42, True]

isBool : HasType (fS fO) ctxt TyBool

isBool = pop stop

Page 15: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

Demonstration: the Interpreter

Page 16: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

Syntax Overloading

dsl notation

dsl expr

lambda = Lam

variable = Var

index_first = stop

index_next = pop

Page 17: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

An Effectful Problem (Haskell)

Evaluator

data Expr = Val Int | Add Expr Expr

eval :: Expr -> Int

eval (Val x) = x

eval (Add x y) = eval x + eval y

Page 18: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

An Effectful Problem (Haskell)

Evaluator

data Expr = Val Int | Add Expr Expr

eval :: Expr -> Int

eval (Val x) = x

eval (Add x y) = eval x + eval y

Page 19: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

An Effectful Problem (Haskell)

Evaluator with variables

data Expr = Val Int | Add Expr Expr

| Var String

type Env = [(String, Int)]

eval :: Expr -> ReaderT Env Maybe Int

eval (Val n) = return n

eval (Add x y) = liftM2 (+) (eval x) (eval y)

eval (Var x) = do env <- ask

val <- lift (lookup x env)

return val

Page 20: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

An Effectful Problem (Haskell)

Evaluator with variables

data Expr = Val Int | Add Expr Expr

| Var String

type Env = [(String, Int)]

eval :: Expr -> ReaderT Env Maybe Int

eval (Val n) = return n

eval (Add x y) = liftM2 (+) (eval x) (eval y)

eval (Var x) = do env <- ask

val <- lift (lookup x env)

return val

Page 21: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

An Effectful Problem (Haskell)

Evaluator with variables

data Expr = Val Int | Add Expr Expr

| Var String

type Env = [(String, Int)]

eval :: Expr -> ReaderT Env Maybe Int

eval (Val n) = return n

eval (Add x y) = liftM2 (+) (eval x) (eval y)

eval (Var x) = do env <- ask

val <- lift (lookup x env)

return val

Page 22: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

An Effectful Problem (Haskell)

Evaluator with variables

data Expr = Val Int | Add Expr Expr

| Var String

type Env = [(String, Int)]

eval :: Expr -> ReaderT Env Maybe Int

eval (Val n) = return n

eval (Add x y) = liftM2 (+) (eval x) (eval y)

eval (Var x) = do env <- ask

val <- lift (lookup x env)

return val

Page 23: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

An Effectful Problem (Haskell)

Evaluator with variables and random numbers

data Expr = Val Int | Add Expr Expr

| Var String

| Random Int

eval :: RandomGen g =>

Expr -> RandT g (ReaderT Env Maybe) Int

...

eval (Var x) = do env <- lift ask

val <- lift (lift (lookup x env))

return val

eval (Random x) = do val <- getRandomR (0, x)

return val

Page 24: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

An Effectful Problem (Haskell)

Evaluator with variables and random numbers

data Expr = Val Int | Add Expr Expr

| Var String

| Random Int

eval :: RandomGen g =>

Expr -> RandT g (ReaderT Env Maybe) Int

...

eval (Var x) = do env <- lift ask

val <- lift (lift (lookup x env))

return val

eval (Random x) = do val <- getRandomR (0, x)

return val

Page 25: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

An Effectful Problem (Haskell)

Evaluator with variables and random numbers

data Expr = Val Int | Add Expr Expr

| Var String

| Random Int

eval :: RandomGen g =>

Expr -> RandT g (ReaderT Env Maybe) Int

...

eval (Var x) = do env <- lift ask

val <- lift (lift (lookup x env))

return val

eval (Random x) = do val <- getRandomR (0, x)

return val

Page 26: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

An Effectful Problem (Haskell)

Challenge — write the following:

dropReader :: RandomGen g =>

RandT g Maybe a ->

RandT g (ReaderT Env Maybe) a

commute :: RandomGen g =>

ReaderT (RandT g Maybe) a ->

RandT g (ReaderT Env Maybe) a

Page 27: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

An Effectful Problem (Idris)

Instead, we could capture everything in one evaluation monad:

Eval monad

EvalState : Type

EvalState = (Int, List (String, Int))

data Eval a

= MkEval (EvalState -> Maybe (a, EvalState))

Page 28: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

An Effectful Problem (Idris)

Eval operations

rndInt : Int -> Int -> Eval Int

get : Eval EvalState

put : EvalState -> Eval ()

Page 29: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

An Effectful Problem (Idris)

Evaluator

eval : Expr -> Eval Int

eval (Val i) = return i

eval (Var x) = do (seed, env) <- get

lift (lookup x env)

eval (Add x y) = [| eval x + eval y |]

eval (Random upper) = do val <- rndInt 0 upper

return val

Page 30: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

Embedded DSLs to the rescue!

Neither solution is satisfying!

Composing monads with transformers becomes hard tomanage

Order matters, but our effects are largely independent

Building one special purpose monad limits reuse

Instead:

We will build an extensible embedded domain specificlanguage (EDSL) to capture algebraic effects and theirhandlers

Page 31: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

The Effect EDSL

The rest of this lecture is about an EDSL, Effect. We will see:

How to use effects

How to implement new effects and handlers

How Effect works

How to express extra-functional correctness properties usingEffect

Page 32: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

Using Effects

Effectful programs

EffM : (m : Type -> Type) ->

List EFFECT -> List EFFECT -> Type -> Type

Eff : (Type -> Type) -> List EFFECT -> Type -> Type

run : Applicative m =>

Env m xs -> EffM m xs xs’ a -> m a

runPure : Env id xs -> EffM id xs xs’ a -> a

Page 33: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

Using Effects

Some Effects

STATE : Type -> EFFECT

EXCEPTION : Type -> EFFECT

STDIO : EFFECT

FILEIO : Type -> EFFECT

RND : EFFECT

Examples

get : Eff m [STATE x] x

putM : y -> EffM m [STATE x] [STATE y] ()

raise : a -> Eff m [EXCEPTION a] b

putStr : String -> Eff IO [STDIO] ()

Page 34: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

Using Effects

Some Effects

STATE : Type -> EFFECT

EXCEPTION : Type -> EFFECT

STDIO : EFFECT

FILEIO : Type -> EFFECT

RND : EFFECT

Examples

get : Eff m [STATE x] x

putM : y -> EffM m [STATE x] [STATE y] ()

raise : a -> Eff m [EXCEPTION a] b

putStr : String -> Eff IO [STDIO] ()

Page 35: Dependently Typed Functional Programming with IdrisDependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Speci c Languages Edwin Brady University of St Andrews

Demonstration