Concepts of programming languages Idris an Pali, Stefan Koppier, - - PowerPoint PPT Presentation

concepts of programming languages
SMART_READER_LITE
LIVE PREVIEW

Concepts of programming languages Idris an Pali, Stefan Koppier, - - PowerPoint PPT Presentation

[ Faculty of Science Information and Computing Sciences] 1 Concepts of programming languages Idris an Pali, Stefan Koppier, Kevin Namink, Luca Scannapieco and Xander van der Goot [ Faculty of Science Information and Computing Sciences]


slide-1
SLIDE 1

[Faculty of Science Information and Computing Sciences] 1

Concepts of programming languages

Idris

Žan Palčič, Stefan Koppier, Kevin Namink, Luca Scannapieco and Xander van der Goot

slide-2
SLIDE 2

[Faculty of Science Information and Computing Sciences] 2

Introduction

Idris is a general purpose pure functional programming language with dependent types. This language supports features that allow it to prove properties of functions. Development of Idris is led by Edwin Brady. Its first stable release was in April 2017.

slide-3
SLIDE 3

[Faculty of Science Information and Computing Sciences] 3

Table of contents

▶ Basics ▶ Dependent types ▶ Type driven programming ▶ Using types to express properties of data ▶ Expressing contracts in types ▶ Conclusions ▶ Questions

slide-4
SLIDE 4

[Faculty of Science Information and Computing Sciences] 4

Basics

Table of contents

▶ Basics ▶ Dependent types ▶ Type driven programming ▶ Using types to express properties of data ▶ Expressing contracts in types ▶ Conclusions ▶ Questions

Basics

▶ Pure functional ▶ Strict ▶ Syntax, similar to Haskell ▶ Total functions ▶ Dependent types

slide-5
SLIDE 5

[Faculty of Science Information and Computing Sciences] 5

Pure functional

Following the definition from “Type driven development with Idris” in a functional programming language:

▶ Programs are composed of functions ▶ Program execution consists of evaluation of functions ▶ Functions are a first-class language construct

And in a pure functional language:

▶ Functions don’t have side effects such as modifying global

variables, throwing exceptions, or preforming console input or

  • utput.

▶ As a result, for any specific inputs, a function will always give the

same result.

slide-6
SLIDE 6

[Faculty of Science Information and Computing Sciences] 5

Pure functional

Following the definition from “Type driven development with Idris” in a functional programming language:

▶ Programs are composed of functions ▶ Program execution consists of evaluation of functions ▶ Functions are a first-class language construct

And in a pure functional language:

▶ Functions don’t have side effects such as modifying global

variables, throwing exceptions, or preforming console input or

  • utput.

▶ As a result, for any specific inputs, a function will always give the

same result.

slide-7
SLIDE 7

[Faculty of Science Information and Computing Sciences] 6

Strict

Idris uses strict evaluation by default but has a Lazy keyword. As example the ifThenElse function in the basic library:

ifThenElse : Bool -> (t: Lazy a) -> (e: Lazy a) -> a ifThenElse True t e = t ifThenElse False t e = e

slide-8
SLIDE 8

[Faculty of Science Information and Computing Sciences] 7

Types

Types are first class citizens.

StringOrInt : Bool -> Type StringOrInt False -> String StringOrInt True -> Int

slide-9
SLIDE 9

[Faculty of Science Information and Computing Sciences] 8

Similarity to other languages

Idris is able to write tactic invocations to prove invariants at compile time, similar to Coq. It is also possible to interactively elaborate with a proof term, similar to Epigram and Agda The syntax of Idris is very similar to Haskell.

slide-10
SLIDE 10

[Faculty of Science Information and Computing Sciences] 9

Syntax differences with Haskell

▶ Colon ▶ Type signature mandatory ▶ Omission of “where” in module declarations ▶ Dependent types

Idris:

foo : Nat -> Nat

Haskell:

foo :: Nat -> Nat

slide-11
SLIDE 11

[Faculty of Science Information and Computing Sciences] 10

Total functions

A way to prove properties of functions. Idris can distinct between functions that are total and that are partial. Total functions are guaranteed to return a value in finite time for every well-typed input.

▶ Total functions return a value in finite time. ▶ Partial functions return a value if it doesn’t crash or enter an

infinite loop. Examples are:

▶ The append function for finite lists is total. ▶ The first function for a list is partial, it is not defined if the list is

empty.

slide-12
SLIDE 12

[Faculty of Science Information and Computing Sciences] 10

Total functions

A way to prove properties of functions. Idris can distinct between functions that are total and that are partial. Total functions are guaranteed to return a value in finite time for every well-typed input.

▶ Total functions return a value in finite time. ▶ Partial functions return a value if it doesn’t crash or enter an

infinite loop. Examples are:

▶ The append function for finite lists is total. ▶ The first function for a list is partial, it is not defined if the list is

empty.

slide-13
SLIDE 13

[Faculty of Science Information and Computing Sciences] 11

Dependent types

Table of contents

▶ Basics ▶ Dependent types ▶ Type driven programming ▶ Using types to express properties of data ▶ Expressing contracts in types ▶ Conclusions ▶ Questions

slide-14
SLIDE 14

[Faculty of Science Information and Computing Sciences] 12

Dependent types

Idris has dependent types, this means types can depend on other values. The most clear example is the Vect type that is used for lists.

slide-15
SLIDE 15

[Faculty of Science Information and Computing Sciences] 13

Dependent types

For example, appending a list of length 3 of strings with a list of length 4

  • f strings:

▶ Simple programming languages would use a type AnyList. ▶ Generic programming languages would instead use List String. ▶ But using dependent types would use the types: Vect 3 String

and Vect 4 String. And return a type Vect 7 String.

slide-16
SLIDE 16

[Faculty of Science Information and Computing Sciences] 14

Type driven programming

Table of contents

▶ Basics ▶ Dependent types ▶ Type driven programming ▶ Using types to express properties of data ▶ Expressing contracts in types ▶ Conclusions ▶ Questions

Type driven programming

▶ Type ▶ Define ▶ Refine

Interactive editing

slide-17
SLIDE 17

[Faculty of Science Information and Computing Sciences] 15

Interactive editing

Type

Defining vectors:

data Vect : Nat -> Type -> Type where Nil : Vect Z a (::) : (x : a) -> (xs : Vect k a) -> Vect (S k) a

slide-18
SLIDE 18

[Faculty of Science Information and Computing Sciences] 16

Interactive editing

Define

Append function with vect data type:

app : Vect n a -> Vect m a -> Vect (n + m) a app Nil ys = ?append1 app (x :: xs) ys = ?append2

slide-19
SLIDE 19

[Faculty of Science Information and Computing Sciences] 17

Interactive editing

Refine

app : Vect n a -> Vect m a -> Vect (n + m) a app Nil ys = ?append1 app (x :: xs) ys = ?append2

Holes are useful because they help us write functions incrementally. We can check the type of ?append2 and ?append1 to find:

append1 : Vect m a append2 : Vect (S (plus len m)) a

  • r search on each of the holes resulting in this:

app : Vect n a -> Vect m a -> Vect (n + m) a app Nil ys = ys app (x :: xs) ys = x :: app xs ys

slide-20
SLIDE 20

[Faculty of Science Information and Computing Sciences] 17

Interactive editing

Refine

app : Vect n a -> Vect m a -> Vect (n + m) a app Nil ys = ?append1 app (x :: xs) ys = ?append2

Holes are useful because they help us write functions incrementally. We can check the type of ?append2 and ?append1 to find:

append1 : Vect m a append2 : Vect (S (plus len m)) a

  • r search on each of the holes resulting in this:

app : Vect n a -> Vect m a -> Vect (n + m) a app Nil ys = ys app (x :: xs) ys = x :: app xs ys

slide-21
SLIDE 21

[Faculty of Science Information and Computing Sciences] 18

What if we can’t make guarantees at compile time?

▶ Dependent pairs

slide-22
SLIDE 22

[Faculty of Science Information and Computing Sciences] 19

What if we can’t make guarantees at compile time?

What we’ve seen works for types which we know at compile time. But what if the size of the vectors are not known at compile time? For example, when we want the user to enter elements on the console until the empty string is entered.

readVect : IO (Vect n String) readVect = do x <- getLine if x == "" then return [] else do xs <- readVect return (x :: xs)

But this won’t type check.

slide-23
SLIDE 23

[Faculty of Science Information and Computing Sciences] 20

Encapsulate Vect in a different type

data VectUnknown : Type -> Type where MkVect : (n : Nat) -> Vect n a -> VectUnknown a readVect : IO (VectUnknown String) readVect = do x <- getLine if x == "" then return (MkVect 0 []) else do MkVect n xs <- readVect return (MkVect (1 + n) (x :: xs))

Now we can construct a VectUnknown which contains the size. As such, we don’t need to know the size of the vector, which we can use to let the vector type check correctly. But this approach results in a lot of boilerplate code if we need to do this for a lot of different types.

slide-24
SLIDE 24

[Faculty of Science Information and Computing Sciences] 20

Encapsulate Vect in a different type

data VectUnknown : Type -> Type where MkVect : (n : Nat) -> Vect n a -> VectUnknown a readVect : IO (VectUnknown String) readVect = do x <- getLine if x == "" then return (MkVect 0 []) else do MkVect n xs <- readVect return (MkVect (1 + n) (x :: xs))

Now we can construct a VectUnknown which contains the size. As such, we don’t need to know the size of the vector, which we can use to let the vector type check correctly. But this approach results in a lot of boilerplate code if we need to do this for a lot of different types.

slide-25
SLIDE 25

[Faculty of Science Information and Computing Sciences] 21

Dependent pairs

The solution of this boilerplate code are dependent pairs, which is a more expressive form of a tuple. We can let the type of the second element be computed from the value of the first. For example:

anyVect : (n : Nat ** Vect n String) anyVect = (3 ** ["Rod", "Jane", "Freddy"])

On a side note, we can even omit the explicit size of the vector, as the type system can figure it out for us:

anyVect : (n : Nat ** Vect n String) anyVect = (_ ** ["Rod", "Jane", "Freddy"])

slide-26
SLIDE 26

[Faculty of Science Information and Computing Sciences] 21

Dependent pairs

The solution of this boilerplate code are dependent pairs, which is a more expressive form of a tuple. We can let the type of the second element be computed from the value of the first. For example:

anyVect : (n : Nat ** Vect n String) anyVect = (3 ** ["Rod", "Jane", "Freddy"])

On a side note, we can even omit the explicit size of the vector, as the type system can figure it out for us:

anyVect : (n : Nat ** Vect n String) anyVect = (_ ** ["Rod", "Jane", "Freddy"])

slide-27
SLIDE 27

[Faculty of Science Information and Computing Sciences] 22

Dependent pair of a number and a vector

Now we can express the same without using an explicitly defined data type.

readVect : IO (n ** Vect n String) readVect = do x <- getLine if x == "" then return (_ ** []) else do (_ ** xs) <- readVect return (_ ** (x :: xs))

slide-28
SLIDE 28

[Faculty of Science Information and Computing Sciences] 23

Using types to express properties of data

Table of contents

▶ Basics ▶ Dependent types ▶ Type driven programming ▶ Using types to express properties of data ▶ Expressing contracts in types ▶ Conclusions ▶ Questions

slide-29
SLIDE 29

[Faculty of Science Information and Computing Sciences] 24

Using types to express properties of data

Dependent types can be used to express properties of data.

▶ Types can express proofs. ▶ Functions in combination with proofs can be used to express

additional properties. Proofs arise naturally from using dependent types.

slide-30
SLIDE 30

[Faculty of Science Information and Computing Sciences] 25

Requiring proofs

We want to check if a vector has the desired length and if so return it

  • therwise return Nothing.

A naive implementation could look like this:

exactLength : (len : Nat ) -> (input : Vect m a)

  • > Maybe (Vect len a)

exactLength {m} len input = if m == len then Just input else Nothing

Error: Type mismatch between Vect m a (Type of input) and Vect len a (Expected type). Specifically: Type mismatch between m and len

slide-31
SLIDE 31

[Faculty of Science Information and Computing Sciences] 25

Requiring proofs

We want to check if a vector has the desired length and if so return it

  • therwise return Nothing.

A naive implementation could look like this:

exactLength : (len : Nat ) -> (input : Vect m a)

  • > Maybe (Vect len a)

exactLength {m} len input = if m == len then Just input else Nothing

Error: Type mismatch between Vect m a (Type of input) and Vect len a (Expected type). Specifically: Type mismatch between m and len

slide-32
SLIDE 32

[Faculty of Science Information and Computing Sciences] 26

Requiring proofs

Idris has no way of knowing whether m and len are equal just by looking at the types. This becomes more apparent if we rewrite it using a case statement:

exactLength : (len : Nat ) -> (input : Vect m a) -> Maybe (Vect len a) exactLength {m} len input = case m == len of True => Just input False => Nothing ▶ Bool does not encode information about the variables m and len in

its type.

slide-33
SLIDE 33

[Faculty of Science Information and Computing Sciences] 27

Expressing proofs

Datatype

data EqNat : (num1 : Nat) -> (num2 : Nat) -> Type where Same : (num : Nat) -> EqNat num num

Valid

the (EqNat 3 3) (Same _)

Invalid

the (EqNat 3 4) (Same _)

Type mismatch between EqNat num num (Type of Same num) and EqNat 3 4 (Expected type) Specifically: Type mismatch between 0 and 1

slide-34
SLIDE 34

[Faculty of Science Information and Computing Sciences] 28

Equality

To express whether two things are equal you can use the built-in function (=).

data (=) : a -> b -> Type where Refl: x = x ▶ Where refl stands for reflexive, being able to express that a type is

equal to it self.

slide-35
SLIDE 35

[Faculty of Science Information and Computing Sciences] 29

Using equality

To replace the comparison we define the function:

checkEqNat : (num1 : Nat) -> (num2 : Nat)

  • > Maybe (num1 = num2)

checkEqNat Z Z = Just Refl checkEqNat Z (S k) = Nothing checkEqNat (S k) Z = Nothing checkEqNat (S k) (S j) = case checkEqNat k j of Nothing => Nothing Just prf => Just (cong prf)

Where cong is a helper function for the inductive step:

cong : {func : a -> b} -> x = y -> func x = func y

slide-36
SLIDE 36

[Faculty of Science Information and Computing Sciences] 30

Basic implementation

Now we can write exactLength using checkEqNat

exactLength : (len : Nat ) -> (input : Vect m a)

  • > Maybe (Vect len a)

exactLength {m} len input = case checkEqNat of Just prf => Just input Nothing => Nothing

slide-37
SLIDE 37

[Faculty of Science Information and Computing Sciences] 31

Equality is not trivial

Lets reintroduce the earlier shown append function but with a slight twist: changing the order of m and n.

app : Vect n a -> Vect m a -> Vect (m + n) a app Nil ys = ys app (x :: xs) ys = x :: app xs ys

Error: Type mismatch between Vect (k+1) elem and Vect (S k) elem (Expected type). Operations are not assumed to be associative, commutative, etc. You need to use rewrite rules to introduces those rules.

slide-38
SLIDE 38

[Faculty of Science Information and Computing Sciences] 31

Equality is not trivial

Lets reintroduce the earlier shown append function but with a slight twist: changing the order of m and n.

app : Vect n a -> Vect m a -> Vect (m + n) a app Nil ys = ys app (x :: xs) ys = x :: app xs ys

Error: Type mismatch between Vect (k+1) elem and Vect (S k) elem (Expected type). Operations are not assumed to be associative, commutative, etc. You need to use rewrite rules to introduces those rules.

slide-39
SLIDE 39

[Faculty of Science Information and Computing Sciences] 32

Empty type

Equality

To express equality we use (=) and Refl.

the (2+2 = 4) (Refl)

Inequality

To express inequality we use the empty type Void.

qm : (2 + 2 = 4 - 1) -> Void qm Refl impossible

slide-40
SLIDE 40

[Faculty of Science Information and Computing Sciences] 32

Empty type

Equality

To express equality we use (=) and Refl.

the (2+2 = 4) (Refl)

Inequality

To express inequality we use the empty type Void.

qm : (2 + 2 = 4 - 1) -> Void qm Refl impossible

slide-41
SLIDE 41

[Faculty of Science Information and Computing Sciences] 33

Decidability

Definition decidability

A property of some values is decidable if you can always say whether the property holds or not for specific values.

Decidability vs Maybe

Maybe allows you to express whether two values are the same but you can’t say the opposite. For this you need a data type for decidability:

data Dec: (prop : Type) -> Type where Yes : (prf : prop) -> Dec prop No : (contra : prop -> Void) -> Dec prop

slide-42
SLIDE 42

[Faculty of Science Information and Computing Sciences] 33

Decidability

Definition decidability

A property of some values is decidable if you can always say whether the property holds or not for specific values.

Decidability vs Maybe

Maybe allows you to express whether two values are the same but you can’t say the opposite. For this you need a data type for decidability:

data Dec: (prop : Type) -> Type where Yes : (prf : prop) -> Dec prop No : (contra : prop -> Void) -> Dec prop

slide-43
SLIDE 43

[Faculty of Science Information and Computing Sciences] 34

Expressing contracts in types

Table of contents

▶ Basics ▶ Dependent types ▶ Type driven programming ▶ Using types to express properties of data ▶ Expressing contracts in types ▶ Conclusions ▶ Questions

Expressing contracts in types

▶ Predicates to describe contracts for function inputs and outputs ▶ Example of takeVect

slide-44
SLIDE 44

[Faculty of Science Information and Computing Sciences] 35

What do we mean for predicates?

Dependent types like EqNat and =, which you saw before, are used entirely for describing relationships between data. If you can construct a value for a predicate, then you know the property described by that predicate must be true.

slide-45
SLIDE 45

[Faculty of Science Information and Computing Sciences] 36

Assumptions and contracts

Who write a function may want to make assumptions on the input.

▶ Remove an element from a vector only under the assumption that

the element is present in the vector

▶ Search for a value in a list only under the assumption that the list

is ordered

▶ Run a database query only under the assumption that the inputs

have been validated They are basically contracts between who write the function and who call it!

slide-46
SLIDE 46

[Faculty of Science Information and Computing Sciences] 37

How to use these predicates

Idris allows to express relationships between data in types. So, you can be explicit about the assumptions you’re making about the inputs to a function. The type checker will do the work of checking these assumptions when the function is called.

slide-47
SLIDE 47

[Faculty of Science Information and Computing Sciences] 38

Example of takeVect

We want to make a function that, given a vector v and a natural number

n, returns the first n elements of v.

Assumption: n must be smaller or equal than the length of v

slide-48
SLIDE 48

[Faculty of Science Information and Computing Sciences] 38

Example of takeVect

We want to make a function that, given a vector v and a natural number

n, returns the first n elements of v.

Assumption: n must be smaller or equal than the length of v

slide-49
SLIDE 49

[Faculty of Science Information and Computing Sciences] 39

This is a simple example

By forcing the length of v to be the sum of n and some Nat m.

takeVect : (n: Nat) -> (v: Vect (n+m) a) -> Vect n a takeVect Z xs = [] takeVect (S k) (x :: xs) = x :: takeVect k xs

slide-50
SLIDE 50

[Faculty of Science Information and Computing Sciences] 40

Let’s complicate things

Express the assumption as an extra parameter of the function. Define a (dependent) type that describes the relationship between two

Nat, the first smaller or equal than the second. data LorE : Nat -> Nat -> Type where Base : LorE Z n Step : LorE n m -> LorE (S n) (S m)

slide-51
SLIDE 51

[Faculty of Science Information and Computing Sciences] 40

Let’s complicate things

Express the assumption as an extra parameter of the function. Define a (dependent) type that describes the relationship between two

Nat, the first smaller or equal than the second. data LorE : Nat -> Nat -> Type where Base : LorE Z n Step : LorE n m -> LorE (S n) (S m)

slide-52
SLIDE 52

[Faculty of Science Information and Computing Sciences] 41

Creating a LorE instance

data LorE : Nat -> Nat -> Type where Base : LorE Z n Step : LorE n m -> LorE (S n) (S m)

Creating the proof that 2 is smaller or equal than 4 is possible

prf: LorE 2 4 prf = Step (Step Base)

slide-53
SLIDE 53

[Faculty of Science Information and Computing Sciences] 42

Creating a LorE instance

data LorE : Nat -> Nat -> Type where Base : LorE Z n Step : LorE n m -> LorE (S n) (S m)

Creating the proof that 4 is smaller or equal than 2 is impossible

fake_prf: LorE 4 2 fake_prf = Step (Step (Step (Step Base))) Type mismatch between LorE (S n) (S m) (Type of Step _) and LorE 1 0 (Expected type)

slide-54
SLIDE 54

[Faculty of Science Information and Computing Sciences] 43

Embedding the proof in the type of the function

takeVect : (m: Nat) -> (v: Vect n a) -> LorE m n -> Vect m a takeVect Z v Base = [] takeVect (S k) (y :: xs) (Step x) = y :: (takeVect k xs x)

slide-55
SLIDE 55

[Faculty of Science Information and Computing Sciences] 44

Calling the function

We need to construct the proof that 3 is smaller or equal than 7 and pass it as third parameter of the function

takeVect 3 [1,2,3,4,5,6,7] (Step (Step (Step Base)))

slide-56
SLIDE 56

[Faculty of Science Information and Computing Sciences] 45

Providing proofs is painful

The need to provide proofs explicitly like this can add a lot of noise to programs and can harm readability. Idris provides a special kind of implicit argument, marked with the keyword auto, to reduce this noise.

slide-57
SLIDE 57

[Faculty of Science Information and Computing Sciences] 46

The auto keyword

takeVect_auto : (m: Nat) -> (v: Vect n a)

  • > {auto prf: LorE m n} -> Vect m a

takeVect_auto Z v {prf = Base} = [] takeVect_auto (S k) (y :: xs) {prf = (Step x)} = y :: (takeVect_auto k xs)

Now we can call the function without providing the proof.

takeVect_auto 3 [1,2,3,4,5,6,7]

Idris will look for the proof with the same mechanism it uses for expression search in interactive editing.

slide-58
SLIDE 58

[Faculty of Science Information and Computing Sciences] 47

Conclusions

Table of contents

▶ Basics ▶ Dependent types ▶ Type driven programming ▶ Using types to express properties of data ▶ Expressing contracts in types ▶ Conclusions ▶ Questions

slide-59
SLIDE 59

[Faculty of Science Information and Computing Sciences] 48

Conclusions

Pro’s:

▶ Familiar syntax, since Haskell is well known. ▶ Produces code with proof of properties. ▶ Dependent types

Con’s:

▶ Still in development, there are missing or not optimized libraries. ▶ Dependent types have a steep learning curve

slide-60
SLIDE 60

[Faculty of Science Information and Computing Sciences] 49

Questions