• Aucun résultat trouvé

An overview of Mezzo

N/A
N/A
Protected

Academic year: 2022

Partager "An overview of Mezzo"

Copied!
169
0
0

Texte intégral

(1)

An overview of Mezzo

François Pottier

INRIA

Bertinoro, June 2015

(2)

Acknowledgements

Jonathan Protzenko, Thibaut Balabonski, Henri Chataing, Armaël Guéneau, Cyprien Mangin.

(3)

What is Mezzo?

Anexperimentalprogramming language in the tradition of ML.

Try it out in your browser:

http://gallium.inria.fr/~protzenk/mezzo-web/

Or install it:

opam install mezzo

(4)

Premise

The types of OCaml, Haskell, Java, C#, etc.:

describe thestructureof data,

but do not distinguishtreesandgraphs,

and do not control who haspermissionto read or write.

(5)

Question

Could a more ambitious static discipline:

rule outmore programming errors, includingdata races,

andenablenew programming idioms,

while remaining reasonablysimpleandflexible?

(6)

A quick comparison

In comparison with Tobias Wrigstad's talk (yesterday),

data race freedomandownership transferare goals too;

getting rid of GC is not;

types and permissionsdo notinfluence code generation;

they are erased at runtime.

(7)

Outline

A first example and a few principles Write-once references: usage Mezzo: (some) design principles

Write-once references: interface & implementation Mezzo: the good and the bad

Algebraic data structures Sharing mutable data Conclusion

(8)

A first example and a few principles

Write-once references: usage

(9)

Write-once references

A write-once reference:

can be writtenat mostonce;

can be read onlyafterit has been written.

Let us look at a concrete example of use...

(10)

Usage

.

writable...

new

.

frozen .

set

.

get

.

open woref

val r1 = new () (* r1 @ writable *) val r2 = r1

(* r1 @ writable * r2 = r1 *) val () = set (r1, 3);

(* r1 @ frozen int * r2 = r1 *) val x2 = get r2

(* r1 @ frozen int * r2 = r1 * x2 @ int *) val rs = (r1, r2)

(* r1 @ frozen int * r2 = r1 * x2 @ int

* rs @ (=r1, =r2) *)

(* rs @ (frozen int, frozen int) *)

. .

Demo!

(11)

Usage

. writable...

new

.

frozen .

set

.

get

.

open woref

val r1 = new () (* r1 @ writable *)

val r2 = r1

(* r1 @ writable * r2 = r1 *) val () = set (r1, 3);

(* r1 @ frozen int * r2 = r1 *) val x2 = get r2

(* r1 @ frozen int * r2 = r1 * x2 @ int *) val rs = (r1, r2)

(* r1 @ frozen int * r2 = r1 * x2 @ int

* rs @ (=r1, =r2) *)

(* rs @ (frozen int, frozen int) *)

. .

Demo!

(12)

Usage

. writable...

new

.

frozen .

set

.

get

.

open woref

val r1 = new () (* r1 @ writable *) val r2 = r1

(* r1 @ writable * r2 = r1 *)

val () = set (r1, 3);

(* r1 @ frozen int * r2 = r1 *) val x2 = get r2

(* r1 @ frozen int * r2 = r1 * x2 @ int *) val rs = (r1, r2)

(* r1 @ frozen int * r2 = r1 * x2 @ int

* rs @ (=r1, =r2) *)

(* rs @ (frozen int, frozen int) *)

. .

Demo!

(13)

Usage

. writable...

new

.

frozen .

set

.

get

.

open woref

val r1 = new () (* r1 @ writable *) val r2 = r1

(* r1 @ writable * r2 = r1 *) val () = set (r1, 3);

(* r1 @ frozen int * r2 = r1 *)

val x2 = get r2

(* r1 @ frozen int * r2 = r1 * x2 @ int *) val rs = (r1, r2)

(* r1 @ frozen int * r2 = r1 * x2 @ int

* rs @ (=r1, =r2) *)

(* rs @ (frozen int, frozen int) *)

. .

Demo!

(14)

Usage

. writable...

new

.

frozen .

set

.

get

.

open woref

val r1 = new () (* r1 @ writable *) val r2 = r1

(* r1 @ writable * r2 = r1 *) val () = set (r1, 3);

(* r1 @ frozen int * r2 = r1 *) val x2 = get r2

(* r1 @ frozen int * r2 = r1 * x2 @ int *)

val rs = (r1, r2)

(* r1 @ frozen int * r2 = r1 * x2 @ int

* rs @ (=r1, =r2) *)

(* rs @ (frozen int, frozen int) *)

. .

Demo!

(15)

Usage

. writable...

new

.

frozen .

set

.

get

.

open woref

val r1 = new () (* r1 @ writable *) val r2 = r1

(* r1 @ writable * r2 = r1 *) val () = set (r1, 3);

(* r1 @ frozen int * r2 = r1 *) val x2 = get r2

(* r1 @ frozen int * r2 = r1 * x2 @ int *) val rs = (r1, r2)

(* r1 @ frozen int * r2 = r1 * x2 @ int

* rs @ (=r1, =r2) *)

(* rs @ (frozen int, frozen int) *)

. .

Demo!

(16)

Usage

. writable...

new

.

frozen .

set

.

get

.

open woref

val r1 = new () (* r1 @ writable *) val r2 = r1

(* r1 @ writable * r2 = r1 *) val () = set (r1, 3);

(* r1 @ frozen int * r2 = r1 *) val x2 = get r2

(* r1 @ frozen int * r2 = r1 * x2 @ int *) val rs = (r1, r2)

(* r1 @ frozen int * r2 = r1 * x2 @ int

* rs @ (=r1, =r2) *)

(* rs @ (frozen int, frozen int) *)

. .

Demo!

(17)

Usage

. writable...

new

.

frozen .

set

.

get

.

open woref

val r1 = new () (* r1 @ writable *) val r2 = r1

(* r1 @ writable * r2 = r1 *) val () = set (r1, 3);

(* r1 @ frozen int * r2 = r1 *) val x2 = get r2

(* r1 @ frozen int * r2 = r1 * x2 @ int *) val rs = (r1, r2)

(* r1 @ frozen int * r2 = r1 * x2 @ int

* rs @ (=r1, =r2) *)

(* rs @ (frozen int, frozen int) *)

. .

Demo!

(18)

A first example and a few principles

Mezzo: (some) design principles

(19)

Permissions

Like a program logic, the static discipline isflow-sensitive.

Acurrent(set of)permission(s) existsat each program point.

Differentpermissions exist at different points.

Permissions do not exist at runtime.

(20)

Permissions

Thus, there is no such thing asthetype of a variablex. Instead,

at each program pointin the scope ofx,

there may bezero, one, or morepermissions to usex

in certain ways.

(21)

Layout and ownership

Permissions havelayoutandownershipreadings.

e.g.,r @ writable

x @ tdescribes theshape and extentof a heap fragment, rooted atx, and describes certainaccess rightsfor it.

“To know aboutx” is “to have access tox” is “to ownx”.

(22)

Just two access modes

Every permission is either duplicable or affine.

The basic rules are:

Immutabledata isduplicable, i.e., shareable.

Mutabledata isaffine, i.e., uniquely owned.

Mutable data can become immutable; not the converse.

(23)

Aliasing

Writinglet x = y in ... gives rise to an equationx = y.

It is a permission: x @ =y, where=yis asingleton type.

In its presence,x @ tandy @ tare interconvertible.

Thus,any name is as good as any other.

The same idea applies tolet x = xs.head in ... .

(24)

Value ̸ = permission

A value can be copied (always). No permission is required.

(* empty *)

let y = (x, x) in (* y @ (=x, =x) *)

(25)

Value ̸ = permission

A duplicable permissioncanbe copied. This is implicit.

(* x @ int *) let y = (x, x) in

(* x @ int * y @ (=x, =x) *)

(* x @ int * y @ (int, int) *)

(26)

Value ̸ = permission

A duplicable permissioncanbe copied. This is implicit.

(* x @ int *) let y = (x, x) in

(* x @ int * y @ (=x, =x) *) (* x @ int * y @ (int, int) *)

(27)

Value ̸ = permission

An affine permissioncannotbe copied.

(* x @ ref int *) let y = (x, x) in

(* x @ ref int * y @ (=x, =x) *)

assert y @ (ref int, ref int) (* WRONG! *)

In other words, mutable data cannot be shared.

(28)

Value ̸ = permission

An affine permissioncannotbe copied.

(* x @ ref int *) let y = (x, x) in

(* x @ ref int * y @ (=x, =x) *)

assert y @ (ref int, ref int) (* WRONG! *)

In other words, mutable data cannot be shared.

(29)

Examples of duplicable versus affine

x @ list intis duplicable: read access can be shared.

x = yis duplicable: equalities are forever.

x @ mlist intandx @ list (ref int)are affine: they give exclusive access to part of the heap.

(30)

Separation

x @ ref int * y @ ref intimpliesxandyare distinct.

Conjunction isseparatingat mutable data.

z @ (t, u)meansz @ (=x, =y) * x @ t * y @ u, forx,yfresh.

Hence, product is separating.

(31)

Separation

The same principle applies to records.

Hence,list (ref int)denotes a list ofdistinctreferences.

Mutable data must betree-structured.

thoughx @ ref (=x)can be written and constructed.

(32)

A first example and a few principles

Write-once references: interface & implementation

(33)

Specification

A usage protocol can be described in a module signature:

Astateis a (user-defined) type.

Atransitionis a (user-defined) function.

(34)

Specification of write-once refs

This protocol has two states and four transitions.

This is the interface fileworef.mzi:

abstract writable ..

abstract frozen a ..

fact duplicable (frozen a) ..

val new: () -> writable ..

val set: [a] (consumes r:..writable,.. x: a | duplicable..a)

-> (| r @ frozen a) ..

val get: [a] frozen a -> a ..

.

(35)

Specification of write-once refs

This protocol has two states and four transitions.

This is the interface fileworef.mzi:

abstract writable ..

abstract frozen a ..

fact duplicable (frozen a) ..

val new: () -> writable ..

val set: [a] (consumes r:..writable,.. x: a | duplicable..a)

-> (| r @ frozen a) ..

val get: [a] frozen a -> a ..

. .

a state

(36)

Specification of write-once refs

This protocol has two states and four transitions.

This is the interface fileworef.mzi:

abstract writable ..

abstract frozen a ..

fact duplicable (frozen a) ..

val new: () -> writable ..

val set: [a] (consumes r:..writable,.. x: a | duplicable..a)

-> (| r @ frozen a) ..

val get: [a] frozen a -> a ..

. .

another state

(37)

Specification of write-once refs

This protocol has two states and four transitions.

This is the interface fileworef.mzi:

abstract writable ..

abstract frozen a ..

fact duplicable (frozen a) ..

val new: () -> writable ..

val set: [a] (consumes r:..writable,.. x: a | duplicable..a)

-> (| r @ frozen a) ..

val get: [a] frozen a -> a ..

. .

implicit transition from frozen to frozen * frozen

(38)

Specification of write-once refs

This protocol has two states and four transitions.

This is the interface fileworef.mzi:

abstract writable ..

abstract frozen a ..

fact duplicable (frozen a) ..

val new: () -> writable ..

val set: [a] (consumes r:..writable,.. x: a | duplicable..a)

-> (| r @ frozen a) ..

val get: [a] frozen a -> a ..

. .

explicit transition into writable

(39)

Specification of write-once refs

This protocol has two states and four transitions.

This is the interface fileworef.mzi:

abstract writable ..

abstract frozen a ..

fact duplicable (frozen a) ..

val new: () -> writable ..

val set: [a] (consumes r:..writable,.. x: a | duplicable..a)

-> (| r @ frozen a) ..

val get: [a] frozen a -> a ..

. .

set requires r (dynamic) and r @ writable (static)

(40)

Specification of write-once refs

This protocol has two states and four transitions.

This is the interface fileworef.mzi:

abstract writable ..

abstract frozen a ..

fact duplicable (frozen a) ..

val new: () -> writable ..

val set: [a] (consumes r:..writable,.. x: a | duplicable..a)

-> (| r @ frozen a) ..

val get: [a] frozen a -> a ..

. .

consumes keyword means r @ writable NOT returned

(41)

Specification of write-once refs

This protocol has two states and four transitions.

This is the interface fileworef.mzi:

abstract writable ..

abstract frozen a ..

fact duplicable (frozen a) ..

val new: () -> writable ..

val set: [a] (consumes r:..writable,.. x: a | duplicable..a)

-> (| r @ frozen a) ..

val get: [a] frozen a -> a ..

. .

duplicable a is a permission

(42)

Specification of write-once refs

This protocol has two states and four transitions.

This is the interface fileworef.mzi:

abstract writable ..

abstract frozen a ..

fact duplicable (frozen a) ..

val new: () -> writable ..

val set: [a] (consumes r:..writable,.. x: a | duplicable..a)

-> (| r @ frozen a) ..

val get: [a] frozen a -> a ..

. .

explicit transition from writable to frozen

(43)

Specification of write-once refs

This protocol has two states and four transitions.

This is the interface fileworef.mzi:

abstract writable ..

abstract frozen a ..

fact duplicable (frozen a) ..

val new: () -> writable ..

val set: [a] (consumes r:..writable,.. x: a | duplicable..a)

-> (| r @ frozen a) ..

val get: [a] frozen a -> a ..

. .

get r requires r @ frozen a

(44)

Implementation

This is the implementation fileworef.mz:

data mutable writable = Writable { contents: ()..}

data frozen a =

Frozen { contents:..(a | duplicable a) }

val new () : writable = Writable { contents = () }

val set [a] (consumes r: writable, x: a | duplicable a) : (| r @ frozen a) =

..r.contents <- x; ..

tag of r <- Frozen (* this is a no-op *)..

val get [a] (r: frozen a) : a = r.contents

.

(45)

Implementation

This is the implementation fileworef.mz:

data mutable writable = Writable { contents: ()..}

data frozen a =

Frozen { contents:..(a | duplicable a) }

val new () : writable = Writable { contents = () }

val set [a] (consumes r: writable, x: a | duplicable a) : (| r @ frozen a) =

..r.contents <- x; ..

tag of r <- Frozen (* this is a no-op *)..

val get [a] (r: frozen a) : a = r.contents

. .

a field of type ()

(46)

Implementation

This is the implementation fileworef.mz:

data mutable writable = Writable { contents: ()..}

data frozen a =

Frozen { contents:..(a | duplicable a) }

val new () : writable = Writable { contents = () }

val set [a] (consumes r: writable, x: a | duplicable a) : (| r @ frozen a) =

..r.contents <- x; ..

tag of r <- Frozen (* this is a no-op *)..

val get [a] (r: frozen a) : a = r.contents

. .

a field of type a

where a must be duplicable

(47)

Implementation

This is the implementation fileworef.mz:

data mutable writable = Writable { contents: ()..}

data frozen a =

Frozen { contents:..(a | duplicable a) }

val new () : writable = Writable { contents = () }

val set [a] (consumes r: writable, x: a | duplicable a) : (| r @ frozen a) =

..r.contents <- x; ..

tag of r <- Frozen (* this is a no-op *)..

val get [a] (r: frozen a) : a = r.contents

. .

initially, r @ writable

(48)

Implementation

This is the implementation fileworef.mz:

data mutable writable = Writable { contents: ()..}

data frozen a =

Frozen { contents:..(a | duplicable a) }

val new () : writable = Writable { contents = () }

val set [a] (consumes r: writable, x: a | duplicable a) : (| r @ frozen a) =

..r.contents <- x; ..

tag of r <- Frozen (* this is a no-op *)..

val get [a] (r: frozen a) : a = r.contents

. .

hence,

r @ Writable { contents: () }

(49)

Implementation

This is the implementation fileworef.mz:

data mutable writable = Writable { contents: ()..}

data frozen a =

Frozen { contents:..(a | duplicable a) }

val new () : writable = Writable { contents = () }

val set [a] (consumes r: writable, x: a | duplicable a) : (| r @ frozen a) =

..r.contents <- x; ..

tag of r <- Frozen (* this is a no-op *)..

val get [a] (r: frozen a) : a = r.contents

. .

after the assignment,

r @ Writable { contents: =x }

(50)

Implementation

This is the implementation fileworef.mz:

data mutable writable = Writable { contents: ()..}

data frozen a =

Frozen { contents:..(a | duplicable a) }

val new () : writable = Writable { contents = () }

val set [a] (consumes r: writable, x: a | duplicable a) : (| r @ frozen a) =

..r.contents <- x; ..

tag of r <- Frozen (* this is a no-op *)..

val get [a] (r: frozen a) : a = r.contents

. .

hence,

r @ Writable { contents: a }

(51)

Implementation

This is the implementation fileworef.mz:

data mutable writable = Writable { contents: ()..}

data frozen a =

Frozen { contents:..(a | duplicable a) }

val new () : writable = Writable { contents = () }

val set [a] (consumes r: writable, x: a | duplicable a) : (| r @ frozen a) =

..r.contents <- x; ..

tag of r <- Frozen (* this is a no-op *)..

val get [a] (r: frozen a) : a = r.contents

. .

after the tag update, r @ Frozen { contents: a }

(52)

Implementation

This is the implementation fileworef.mz:

data mutable writable = Writable { contents: ()..}

data frozen a =

Frozen { contents:..(a | duplicable a) }

val new () : writable = Writable { contents = () }

val set [a] (consumes r: writable, x: a | duplicable a) : (| r @ frozen a) =

..r.contents <- x; ..

tag of r <- Frozen (* this is a no-op *)..

val get [a] (r: frozen a) : a = r.contents

. .

hence, r @ frozen a

(53)

A first example and a few principles

Mezzo: the good and the bad

(54)

The good

The uniqueness of read/write permissions:

rules outseveral categories of errors:

data races; hence,shared-memory concurrency is safe;

representation exposure;

violations of (certain) object protocols.

allowsthe type of an object to vary with time, which enables:

explicit memory re-use;

gradual initialization;

describing (certain) object protocols.

(55)

The good

Here are some other positive aspects:

all of thepowerof ML, and more;

higher-order functions, pattern matching, polymorphism, etc.

no need to annotate types with owners;

to have a permission is to own

ownership transferis easy;

just pass (or return, or store, or extract) a permission

no need to annotate function types with effects.

just pass and return a permission

(56)

The good

Moving an elementintoorout ofa container is easy.

Here is a typical container interface:

abstract bag a

val new: [a] () -> bag a

val insert: [a] (bag a, consumes a) -> () val extract: [a] bag a -> option a

(57)

The bad

The disciplineforbids sharingmutable data.

For this reason,borrowingan element from a container is typically restricted toduplicableelements:

val find:

[a]

duplicable a =>

(a -> bool) -> list a -> option a

This affects user-defined data structures, arrays, regions, etc.

(58)

The bad

Fortunately,

there isno restrictionon the use of immutable data;

there areseveral waysof sharing mutable data:

(static) nesting; regions;

(dynamic) adoption & abandon;

(dynamic) locks.

(59)

Outline

A first example and a few principles Algebraic data structures

(More) Principles

Computing the length of a list Melding mutable lists

Concatenating immutable lists Sharing mutable data

Conclusion

(60)

Algebraic data structures

(More) Principles

(61)

Immutable lists

The algebraic data type of immutable lists is defined as in ML:

data list a =

| Nil

| Cons { head: a; tail: list a }

(62)

Mutable lists

To define a type of mutable lists, one adds a keyword:

data mutable mlist a =

| MNil

| MCons { head: a; tail: mlist a }

(63)

Examples

For instance,

x @ list intprovides (read) access to an immutable list of integers, rooted atx.

x @ mlist intprovides (exclusive, read/write) access to a mutable list of integers atx.

x @ list (ref int)offers read access to the spine and read/write access to the elements, which are distinct cells.

(64)

Permission refinement

Permission refinement takes place at case analysis.

..

match xs with

| MNil ->

..

...

| MCons ->

..

let x = xs.head in

..

...

end

In contrast, traditional separation logic hasuntaggedunion.

.

(65)

Permission refinement

Permission refinement takes place at case analysis.

..

match xs with

| MNil ->

..

...

| MCons ->

..

let x = xs.head in

..

...

end

In contrast, traditional separation logic hasuntaggedunion.

. .

a nominal permission:

xs @ mlist a

(66)

Permission refinement

Permission refinement takes place at case analysis.

..

match xs with

| MNil ->

..

...

| MCons ->

..

let x = xs.head in

..

...

end

In contrast, traditional separation logic hasuntaggedunion.

. .

a structural permission:

xs @ MNil

(67)

Permission refinement

Permission refinement takes place at case analysis.

..

match xs with

| MNil ->

..

...

| MCons ->

..

let x = xs.head in

..

...

end

In contrast, traditional separation logic hasuntaggedunion.

. .

another structural permission:

xs @ MCons { head: a; tail: mlist a }

(68)

Permission refinement

Permission refinement takes place at case analysis.

..

match xs with

| MNil ->

..

...

| MCons ->

..

let x = xs.head in

..

...

end

In contrast, traditional separation logic hasuntaggedunion.

. .

automatically expanded to:

xs @ MCons { head: (=h); tail: (=t) }

* h @ a

* t @ mlist a

(69)

Permission refinement

Permission refinement takes place at case analysis.

..

match xs with

| MNil ->

..

...

| MCons ->

..

let x = xs.head in

..

...

end

In contrast, traditional separation logic hasuntaggedunion.

. .

or (sugar):

xs @ MCons { head = h; tail = t }

* h @ a

* t @ mlist a

(70)

Permission refinement

Permission refinement takes place at case analysis.

..

match xs with

| MNil ->

..

...

| MCons ->

..

let x = xs.head in

..

...

end

In contrast, traditional separation logic hasuntaggedunion.

. .

so, after the read access:

xs @ MCons { head = h; tail = t }

* h @ a

* t @ mlist a

* x = h

(71)

Principles

This illustrates two mechanisms:

A nominal permission can beunfoldedandrefined, yielding a structural permission.

A structural permission can bedecomposed,

yielding separate permissions for the block and its fields.

These reasoning steps are implicit and reversible.

(72)

Algebraic data structures

Computing the length of a list

(73)

Interface

Here is the type of thelengthfunction for mutable lists.

val length: [a] mlist a -> int

It should be understood as follows:

lengthrequires one argumentxs,

along with the permissionxs @ mlist a.

lengthreturns one resultn,

along with the permissionxs @ mlist a * n @ int.

(74)

Implementation

val rec length_aux [a] (accu: int, xs: mlist a) : int = match xs with..

| MNil ->..

accu..

| MCons ->..

length_aux (accu + 1, xs.tail)..

end

val length [a] (xs: mlist a) : int = length_aux (0, xs)

.

(75)

Implementation

val rec length_aux [a] (accu: int, xs: mlist a) : int = match xs with..

| MNil ->..

accu..

| MCons ->..

length_aux (accu + 1, xs.tail)..

end

val length [a] (xs: mlist a) : int = length_aux (0, xs)

. .

initially:

xs @ mlist a

(76)

Implementation

val rec length_aux [a] (accu: int, xs: mlist a) : int = match xs with..

| MNil ->..

accu..

| MCons ->..

length_aux (accu + 1, xs.tail)..

end

val length [a] (xs: mlist a) : int = length_aux (0, xs)

. .

upon entry into the first branch:

xs @ MNil

(77)

Implementation

val rec length_aux [a] (accu: int, xs: mlist a) : int = match xs with..

| MNil ->..

accu..

| MCons ->..

length_aux (accu + 1, xs.tail)..

end

val length [a] (xs: mlist a) : int = length_aux (0, xs)

. .

upon exit of the first branch:

xs @ MNil

(78)

Implementation

val rec length_aux [a] (accu: int, xs: mlist a) : int = match xs with..

| MNil ->..

accu..

| MCons ->..

length_aux (accu + 1, xs.tail)..

end

val length [a] (xs: mlist a) : int = length_aux (0, xs)

. .

upon exit of the first branch:

xs @ mlist a

(79)

Implementation

val rec length_aux [a] (accu: int, xs: mlist a) : int = match xs with..

| MNil ->..

accu..

| MCons ->..

length_aux (accu + 1, xs.tail)..

end

val length [a] (xs: mlist a) : int = length_aux (0, xs)

. .

upon entry into the second branch:

xs @ MCons { head = h; tail = t } h @ a

t @ mlist a

(80)

Implementation

val rec length_aux [a] (accu: int, xs: mlist a) : int = match xs with..

| MNil ->..

accu..

| MCons ->..

length_aux (accu + 1, xs.tail)..

end

val length [a] (xs: mlist a) : int = length_aux (0, xs)

. .

after the call, nothing has changed:

xs @ MCons { head = h; tail = t } h @ a

t @ mlist a

(81)

Implementation

val rec length_aux [a] (accu: int, xs: mlist a) : int = match xs with..

| MNil ->..

accu..

| MCons ->..

length_aux (accu + 1, xs.tail)..

end

val length [a] (xs: mlist a) : int = length_aux (0, xs)

. .

thus, by recombining:

xs @ MCons { head: a; tail: mlist a }

(82)

Implementation

val rec length_aux [a] (accu: int, xs: mlist a) : int = match xs with..

| MNil ->..

accu..

| MCons ->..

length_aux (accu + 1, xs.tail)..

end

val length [a] (xs: mlist a) : int = length_aux (0, xs)

. .

thus, by folding:

xs @ mlist a

(83)

Tail recursion versus iteration

The analysis of this code is surprisingly simple.

This is atail-recursivefunction, i.e., a loop in disguise.

As we go, there is alistahead of us and alist segmentbehind us.

Ownership of the latter isimplicit, i.e., framed out.

Recursive reasoning, iterative execution.

(84)

Algebraic data structures

Melding mutable lists

(85)

Melding mutable lists (1/2)

val rec meld_aux [a]

(xs:..MCons { head: a; tail: mlist a },

consumes..ys: mlist a) : () = match xs.tail with

| MNil ->..

xs.tail <- ys..

| MCons ->..

meld_aux (xs.tail, ys)..

end

.

(86)

Melding mutable lists (1/2)

val rec meld_aux [a]

(xs:..MCons { head: a; tail: mlist a },

consumes..ys: mlist a) : () = match xs.tail with

| MNil ->..

xs.tail <- ys..

| MCons ->..

meld_aux (xs.tail, ys)..

end

. .

xs is not consumed: at the end, it is still a valid non-empty list

(87)

Melding mutable lists (1/2)

val rec meld_aux [a]

(xs:..MCons { head: a; tail: mlist a },

consumes..ys: mlist a) : () = match xs.tail with

| MNil ->..

xs.tail <- ys..

| MCons ->..

meld_aux (xs.tail, ys)..

end

. .

at the end, ys is accessible through xs, hence must no longer be used directly

(88)

Melding mutable lists (1/2)

val rec meld_aux [a]

(xs:..MCons { head: a; tail: mlist a },

consumes..ys: mlist a) : () = match xs.tail with

| MNil ->..

xs.tail <- ys..

| MCons ->..

meld_aux (xs.tail, ys)..

end

. .

xs @ MCons { head: a; tail = t } t @ MNil

ys @ mlist a

(89)

Melding mutable lists (1/2)

val rec meld_aux [a]

(xs:..MCons { head: a; tail: mlist a },

consumes..ys: mlist a) : () = match xs.tail with

| MNil ->..

xs.tail <- ys..

| MCons ->..

meld_aux (xs.tail, ys)..

end

. .

xs @ MCons { head: a; tail = ys } t @ MNil

ys @ mlist a

Références

Documents relatifs

In the current design, the platform offers six different types of basic data operations for which Web APIs have been designed: DB-RDFization (for mapping data from re- lational

This tutorial will present the work that has been done in the context of one of the Spanish normalization working groups towards the definition of an open data maturity model

Building on that experience, we have now transformed additional multilingual terminological resources, namely, a set of freely available terminology databases 2 from the Catalan

Data management in grid environments. Data management in grid en- vironments is currently a topic of major interest to the grid computing com- munity. However, as of today, no

Co-simulation is provided through the use of a common format, and automatic distributed code generation is experimented in the context of the graphical environment of the

We presented organic data sharing as a novel approach to collect dark data from the long tail of science in a form that can be enticing to scientists and

Handling and sharing geospatial data from etherogeneous sources in a collaborative way can be addressed by a WebGIS platform, built with open source components.. An important role

Our approach: transparent access to data A growing number of applications make use of larger and larger amounts of distributed data. We claim that explicit management of data