Dependently Typed Functional Programming with Idris Dependently Typed Functional Programming with...

download Dependently Typed Functional Programming with Idris Dependently Typed Functional Programming with Idris

of 35

  • date post

    23-Sep-2020
  • Category

    Documents

  • view

    2
  • download

    0

Embed Size (px)

Transcript of Dependently Typed Functional Programming with Idris Dependently Typed Functional Programming with...

  • Dependently Typed Functional Programming with Idris

    Lecture 2: Embedded Domain Specific Languages

    Edwin Brady University of St Andrews

    ecb10@st-andrews.ac.uk

    @edwinbrady

    ecb10@st-andrews.ac.uk @edwinbrady

  • DSLs in Idris

    Idris aims to support the implementation of verified domain specific languages. To illustrate this, we begin with an interpreter for the simply typed λ-calculus.

  • 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

  • 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

  • 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

  • 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

  • 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

  • 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

  • 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

  • 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

  • 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 index index i G is a bounds safe lookup of variable i in context G.

  • 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 index index i G is a bounds safe lookup of variable i in context G.

  • 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

  • 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

  • Demonstration: the Interpreter

  • Syntax Overloading

    dsl notation

    dsl expr

    lambda = Lam

    variable = Var

    index_first = stop

    index_next = pop

  • 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

  • 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

  • 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

  • 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

  • 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

  • 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

  • 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

  • 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

  • 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

  • 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

  • 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))

  • An Effectful Problem (Idris)

    Eval operations

    rndInt : Int -> Int -> Eval Int

    get : Eval EvalState

    put : EvalState -> Eval ()

  • An Effectful Problem (Idris)

    Evaluator

    eval : Expr -> Eval Int

    eval (Val i) = return i

    eval (Var x) = do (seed, env)

  • Embedded DSLs to the rescue!

    Neither solution is satisfying!

    Composing monads with transformers becomes hard to manage

    Order matters, but our effects are largely independent

    Building one special purpose monad limits reuse

    Instead:

    We will build an extensible embedded domain specific language (EDSL) to capture algebraic effects and their handlers

  • 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 using Effect

  • 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

  • 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] ()

  • Using Effects

    Some Effects

    STATE : Type -> EFFECT

    EXCEPTION : Type -> EFFE