Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with...

51
Idris: General Purpose Programming with Dependent Types Edwin Brady University of St Andrews [email protected] @edwinbrady 27th June 2013

Transcript of Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with...

Page 1: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Idris: General Purpose Programming withDependent Types

Edwin BradyUniversity of St Andrews

[email protected]

@edwinbrady

27th June 2013

Page 2: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Introduction

Idris is a pure functional programming language with dependenttypes

cabal update; cabal install idris

http://idris-lang.org

In this talk:

An introduction to the language

Extended examples

Page 3: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Introduction

Idris is a pure functional programming language with dependenttypes

cabal update; cabal install idris

http://idris-lang.org

In this talk:

An introduction to the language

Extended examples

Page 4: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values
Page 5: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Software Correctness

Page 6: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Software Correctness

Page 7: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Idris Overview

General purpose programming language

Compiled, supports foreign functions, . . .

Influenced by Haskell

Pattern matching, where, . . .Type classes, do-notation, comprehensions, . . .

Has full dependent types

Types may be predicated on valuesCan encode (and check) program propertiesSupports tactic based theorem proving

Page 8: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Idris Overview

General purpose programming language

Compiled, supports foreign functions, . . .

Influenced by Haskell

Pattern matching, where, . . .Type classes, do-notation, comprehensions, . . .

Has full dependent types

Types may be predicated on valuesCan encode (and check) program propertiesSupports tactic based theorem proving

Page 9: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Idris Overview

General purpose programming language

Compiled, supports foreign functions, . . .

Influenced by Haskell

Pattern matching, where, . . .Type classes, do-notation, comprehensions, . . .

Has full dependent types

Types may be predicated on valuesCan encode (and check) program propertiesSupports tactic based theorem proving

Page 10: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Dependent Types in Idris

Unary natural numbers

data Nat = O | S Nat

Polymorphic lists

data List : Type -> Type where

Nil : List a

(::) : a -> List a -> List a

Vectors — polymorphic lists with length

data Vect : Type -> Nat -> Type where

Nil : Vect a O

(::) : a -> Vect a k -> Vect a (S k)

Page 11: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Dependent Types in Idris

Unary natural numbers

data Nat = O | S Nat

Polymorphic lists

data List : Type -> Type where

Nil : List a

(::) : a -> List a -> List a

Vectors — polymorphic lists with length

data Vect : Type -> Nat -> Type where

Nil : Vect a O

(::) : a -> Vect a k -> Vect a (S k)

Page 12: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Dependent Types in Idris

Unary natural numbers

data Nat = O | S Nat

Polymorphic lists

data List : Type -> Type where

Nil : List a

(::) : a -> List a -> List a

Vectors — polymorphic lists with length

data Vect : Type -> Nat -> Type where

Nil : Vect a O

(::) : a -> Vect a k -> Vect a (S k)

Page 13: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Dependent Types — Examples

Append

(++) : Vect a m -> Vect a n -> Vect a (m + n)

(++) [] ys = ys

(++) (x::xs) ys = x :: xs ++ ys

Pairwise addition

vAdd : Num a => Vect a n -> Vect a n -> Vect a n

vAdd [] [] = []

vAdd (x :: xs) (y :: ys) = x + y :: vAdd xs ys

Page 14: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Dependent Types — Examples

Append

(++) : Vect a m -> Vect a n -> Vect a (m + n)

(++) [] ys = ys

(++) (x::xs) ys = x :: xs ++ ys

Pairwise addition

vAdd : Num a => Vect a n -> Vect a n -> Vect a n

vAdd [] [] = []

vAdd (x :: xs) (y :: ys) = x + y :: vAdd xs ys

Page 15: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Dependent Types — Examples

Append

(++) : Vect a m -> Vect a n -> Vect a (m + n)

(++) [] ys = ys

(++) (x::xs) ys = x :: xs ++ ys

Pairwise addition

total

vAdd : Num a => Vect a n -> Vect a n -> Vect a n

vAdd [] [] = []

vAdd (x :: xs) (y :: ys) = x + y :: vAdd xs ys

Page 16: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Why Dependent Types?

Precise types

sort : List Int -> List Int

Page 17: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Why Dependent Types?

Precise types

sort : Vect Int n -> Vect Int n

Page 18: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Why Dependent Types?

Precise types

sort : (xs : Vect Int n) ->

(ys : Vect Int n ** Permutation xs ys)

Page 19: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Why Dependent Types?

Precise types

sort : (xs : Vect Int n) ->

(ys : Vect Int n ** Permutation xs ys)

We can make types as precise as we require.

However, precise types may require compleximplementations/proofs.

Page 20: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Why Dependent Types?

In this lecture, we will see examples of:

Precise types for supporting machine-checked proofs ofcorrectness

Generic programming

First class types, so types can be calculated by programs

Page 21: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Why Dependent Types?

In this lecture, we will see examples of:

Precise types for supporting machine-checked proofs ofcorrectness

Generic programming

First class types, so types can be calculated by programs

Page 22: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Demonstration

Page 23: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Conclusion

Idris is a work in progress. We’re having lots of fun:

Implementing domain specific languages

Investigating scientific programming applications

Writing new code generators: Javascript, LLVM, . . .

Investigating network and security protocol verification

But there’s lots to do, and you can get involved:

Libraries (algorithms, data structures, proofs), genericprogramming, deriving, interactive development, automatedtheorem proving, more C bindings, . . .

cabal install idris and enjoy. . .

Page 24: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Extra slides

Page 25: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

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 26: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

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 27: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

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 28: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

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 29: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

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 30: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

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 31: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

An Effectful Problem (Haskell)

Evaluator with variables and state

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 32: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

An Effectful Problem (Haskell)

Evaluator with variables and state

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 33: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

An Effectful Problem (Haskell)

Evaluator with variables and state

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 34: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

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 35: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

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 36: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

An Effectful Problem (Idris)

Eval operations

rndInt : Int -> Int -> Eval Int

get : Eval EvalState

put : EvalState -> Eval ()

Page 37: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

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 38: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

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 39: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

The Effect EDSL

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

How to use effects

How to implement new effects and handlers

How Effect works

Page 40: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

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 41: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

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 42: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

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 43: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Demonstration — effects

Page 44: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Compilation

Recent comment on http://www.reddit.com/r/programming:

“There are also all kinds of issues with the complexityand performance of compiling languages that havedependent types.”

(http://www.xkcd.com/386 — Duty Calls)

Page 45: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Compilation

Recent comment on http://www.reddit.com/r/programming:

“There are also all kinds of issues with the complexityand performance of compiling languages that havedependent types.”

(http://www.xkcd.com/386 — Duty Calls)

Page 46: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Phase Distinctions

Conventionally seen as separation of types and terms

Erase types before executing a programPerceived complexity with dependent types: what is a type andwhat is a term?

Really separation of compile time and run time

Erase compile time only terms before executing a programConventionally compile time only = typesDistinction is harder to make with dependent types

but Idris does!

Page 47: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Phase Distinctions

Conventionally seen as separation of types and terms

Erase types before executing a programPerceived complexity with dependent types: what is a type andwhat is a term?

Really separation of compile time and run time

Erase compile time only terms before executing a programConventionally compile time only = types

Distinction is harder to make with dependent types

but Idris does!

Page 48: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Phase Distinctions

Conventionally seen as separation of types and terms

Erase types before executing a programPerceived complexity with dependent types: what is a type andwhat is a term?

Really separation of compile time and run time

Erase compile time only terms before executing a programConventionally compile time only = typesDistinction is harder to make with dependent types

but Idris does!

Page 49: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Phase Distinctions in Idris

The compiler erases all values which it can prove are unused atrun-time, by:

Forcing

Erase constructor arguments with value determined by another

Collapsing

Erase data type with only one inhabitantRelies on totalityTypically erases equality proofs, predicates, . . .

Identifying unused arguments

Erase function and constructor arguments which are neverinspected

Page 50: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Phase Distinctions in Idris

The compiler erases all values which it can prove are unused atrun-time, by:

Forcing

Erase constructor arguments with value determined by another

Collapsing

Erase data type with only one inhabitantRelies on totalityTypically erases equality proofs, predicates, . . .

Identifying unused arguments

Erase function and constructor arguments which are neverinspected

Page 51: Idris: General Purpose Programming with Dependent Types · Distinction is harder to make with dependent types but Idris does! Phase Distinctions in Idris The compiler erases all values

Phase Distinctions in Idris

The compiler erases all values which it can prove are unused atrun-time, by:

Forcing

Erase constructor arguments with value determined by another

Collapsing

Erase data type with only one inhabitantRelies on totalityTypically erases equality proofs, predicates, . . .

Identifying unused arguments

Erase function and constructor arguments which are neverinspected