45
Dependently Typed Functional Programming with Idris Lecture 2: Embedded Domain Specific Languages Edwin Brady University of St Andrews [email protected] @edwinbrady

Dependently Typed Functional Programming with Idris - Edwin Brady

  • Upload
    others

  • View
    10

  • Download
    0

Embed Size (px)

Citation preview

Dependently Typed Functional Programming withIdris

Lecture 2: Embedded Domain Specific Languages

Edwin BradyUniversity of St Andrews

[email protected]

@edwinbrady

Introduction

In this lecture:

Some history

Programming Languages and Abstraction

A well-typed interpreter

Embedding languages with syntax overloading

OS interaction and foreign functions

Managing resources

Abstraction

Computer Scientists often talk of abstraction

English definition: “the process of considering somethingindependently of its associations or attributes”

In Computer Science:

“. . . a process of extracting the general structure to allow theinessential details to be ignored”— Ron Morrison, PhD ThesisSeparation of what a program or system does from how

We can view a computer system as a number of layers ofabstraction

Abstraction

Computer Scientists often talk of abstraction

English definition: “the process of considering somethingindependently of its associations or attributes”

In Computer Science:

“. . . a process of extracting the general structure to allow theinessential details to be ignored”— Ron Morrison, PhD ThesisSeparation of what a program or system does from how

We can view a computer system as a number of layers ofabstraction

Some History

Some History

1946 — ENIAC, the first general purpose electronic computer

Turing Complete — meaning it could run any computablefunction

But. . . no abstraction!

Programmed by manipulating switches and cables on themachine

Some History

1949 — EDSAC, the first practical stored program computer

Programs stored in memory as binary data

1950 (approx) — Assembly Language

Mnemonics for machine instructionsMOV A, R4

ADD B, R4

MOV R4, C

HALT

New abstraction: human readable notation for machinelanguage

Some History

1949 — EDSAC, the first practical stored program computer

Programs stored in memory as binary data

1950 (approx) — Assembly Language

Mnemonics for machine instructionsMOV A, R4

ADD B, R4

MOV R4, C

HALT

New abstraction: human readable notation for machinelanguage

Some History

Some History

1952 — A-0, the first compiler

“Arithmetic Language version 0”

Converted a specification into machine code

Created by Grace Hopper

“She did this, she said, because she was lazy and hoped that theprogrammer may return to being a mathematician.”(http://www.sdsc.edu/ScienceWomen/hopper.html)

Some History

1952 — A-0, the first compiler

“Arithmetic Language version 0”

Converted a specification into machine code

Created by Grace Hopper

“She did this, she said, because she was lazy and hoped that theprogrammer may return to being a mathematician.”(http://www.sdsc.edu/ScienceWomen/hopper.html)

Some History

Some History

1957 — FORTRAN (FORmula TRANslator)

Intended as a more practical alternative to hand-writingassembly language

Invented by a team led by John Backus

New abstraction: Familiar mathematical notation

C = A + B

“Much of my work has come from being lazy. I didn’t like writingprograms, and so, when I was working on the IBM 701, writingprograms for computing missile trajectories, I started work on aprogramming system to make it easier to write programs.”— John Backus

Some History

1957 — FORTRAN (FORmula TRANslator)

Intended as a more practical alternative to hand-writingassembly language

Invented by a team led by John Backus

New abstraction: Familiar mathematical notation

C = A + B

“Much of my work has come from being lazy. I didn’t like writingprograms, and so, when I was working on the IBM 701, writingprograms for computing missile trajectories, I started work on aprogramming system to make it easier to write programs.”— John Backus

High Level Languages

Since FORTRAN, many new high level languages have beendesigned — providing new abstractions, targetting new applicationdomains. A selection:

LISP (1958), ALGOL (1960), C (1973), ML (1973), C++(1983), Haskell (1990), Java (1995), Go (2009)

“ Most programming languages are partly a way of expressingthings in terms of other things and partly a basic set of giventhings.”

— Peter Landin, “The next 700 programming languages” (1966)

High Level Languages

Since FORTRAN, many new high level languages have beendesigned — providing new abstractions, targetting new applicationdomains. A selection:

LISP (1958), ALGOL (1960), C (1973), ML (1973), C++(1983), Haskell (1990), Java (1995), Go (2009)

“ Most programming languages are partly a way of expressingthings in terms of other things and partly a basic set of giventhings.”

— Peter Landin, “The next 700 programming languages” (1966)

Domain Specific Languages

A Domain Specific Language (DSL) is a language designed fora particular problem domain

Very high level of abstractionTypically declarative, i.e. say what, not howOften not Turing Complete

Examples:

Database and Internet applications — HTML, XML, SQL, . . .Scientific programming — R, MathematicaComputer games — UnrealScriptHardware description — VerilogSpreadsheet formulas

Domain Specific Languages

A Domain Specific Language (DSL) is a language designed fora particular problem domain

Very high level of abstractionTypically declarative, i.e. say what, not howOften not Turing Complete

Email filtering:

Domain Specific Languages

A Domain Specific Language (DSL) is a language designed fora particular problem domain

Very high level of abstractionTypically declarative, i.e. say what, not howOften not Turing Complete

Music playlists

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.

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 indexindex 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 indexindex 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

Syntax Overloading

syntax rules

syntax IF [x] THEN [t] ELSE [e] = If x t e

forLoop : List a -> (a -> IO ()) -> IO ()

syntax for {x} "in" [xs] ":" [body]

= forLoop xs (\x => body)

Syntax Overloading

Implicit conversions

data Lang : Vect Ty n -> Ty -> Type where

Val : interpTy a -> Lang G a

...

implicit MkVal : interpTy a -> Lang G a

MkVal = Val

Interlude: Foreign Function Calls

Foreign Types

data FTy = FInt | FFloat | FChar | FString

| FPtr | FAny Type | FUnit

Foreign Types to Idris

interpFTy : FTy -> Type

interpFTy FInt = Int

interpFTy FFloat = Float

interpFTy FChar = Char

interpFTy FString = String

interpFTy FPtr = Ptr

interpFTy (FAny t) = t

interpFTy FUnit = ()

Interlude: Foreign Function Calls

Foreign Types

data FTy = FInt | FFloat | FChar | FString

| FPtr | FAny Type | FUnit

Foreign Types to Idris

interpFTy : FTy -> Type

interpFTy FInt = Int

interpFTy FFloat = Float

interpFTy FChar = Char

interpFTy FString = String

interpFTy FPtr = Ptr

interpFTy (FAny t) = t

interpFTy FUnit = ()

Interlude: Foreign Function Calls

Building Foreign Function Calls

ForeignTy : (xs:List FTy) -> (t:FTy) -> Type

data Foreign : Type -> Type where

FFun : String -> (xs:List FTy) -> (t:FTy) ->

Foreign (ForeignTy xs t)

mkForeign : Foreign x -> x

Interlude: Foreign Function Calls

Examples

putStr : String -> IO ()

putStr x

= mkForeign (FFun "putStr" [FString] FUnit) x

getChar : IO Char

getChar = mkForeign (FFun "getchar" [] FChar)

Interlude: Foreign Function Calls

Examples

data File = FHandler Ptr

do_fopen : String -> String -> IO Ptr

do_fopen f m

= mkForeign

(FFun "fileOpen" [FString, FString] FPtr) f m

openFile : String -> Mode -> IO File

openFile f m = fopen f (modeStr m) where

modeStr Read = "r"

modeStr Write = "w"

modeStr ReadWrite = "r+"

Extra-functional Correctness

“Nobody in industry really cares whether a program does what it’ssupposed to – what they are really concerned with is how theprogram behaves when it’s run.”

— Joe Armstrong (designer of Erlang programming language)

Extra-functional Correctness

We make a distinction between:

Functional correctness

Does the program do what it is supposed to do?e.g. Does sort produce an ordered permutation of its input?

Extra-functional correctness

Does the program run within the required resource constraints?e.g. Does the program run within 1 Mb RAM? Does it respondto input within 1 second?

Resource Example: Files

A program which manages files must conform to a resource usageprotocol. Informally:

Files must be opened succesfully before they are read orwritten

A file open for reading may not be written to, and vice versa

Files must be closed when processing is finished

We have developed a domain specific language for encoding, ingeneral, resource usage protocols

Demonstration: Resources

Resource Safe File Management

readH : String -> RES ()

readH fn = res (do let x = open fn Reading

if opened x then

do str <- rreadLine x

rputStrLn str

rclose x

else rputStrLn "Error")