## Effectful Programming in F*

Microsoft Research

Oregon Programming Languages Summer School (OPLSS), 2021

### Shallow embeddings of effectful languages

• Last time, we saw how to deeply embed small languages within F*, develop their metatheory, and proof tools to use on them

• This time: How can we embed full-fledged, effectful programming languages within F*?

### General approach to effectful programming

• Program libraries to model effects

• Derive effectful actions for primitive operations

• read, write, alloc, throw, catch, fork, join, etc.
• Write programs against these models and verify them with refined computation types

• Extract them to OCaml, F#, C, Wasm, with primitive effects

• F*:

let incr (r:ref int) : ST unit
(ensures fun h0 _ h1 -> modifies {r} h0 h1 /\ h1.[r] = h0.[r] + 1)
= r := !r + 1

ML:

let incr (r:ref int) : unit = r := !r + 1

C:

void incr (int *r) { *r = *r + 1; }

### Values and Computations

• “Values” aka unconditionally total terms

• Two classes of types

• Value types (t): int, list int,
• Computation types (C): pure, divergent, stateful,
• Dependent function types of the form: x:t -> C

• F* is call-by-value
• argument can't have side-effects, so value type
• Two forms of refinement types

• Refined value types: x:t{p}
• Refined computation types:
• Stateful computations that can read and write the heap: ST t pre post

type st (a:Type) = int -> (a * int)
new_effect {
STATE : a:Type -> Effect
with repr = st;
return = fun (a:Type) (x:a) (h:int) -> x, h;
bind = fun (a b:Type) (f:st a) (g:a -> st b) (h:int) ->
let z, h' = f h in g z h';
get = fun () (h:int) -> h,h;
put = fun (h:int) _ -> (),h
}
• this monadic definition is the model F* uses to verify stateful code

• It is an executable model, and you can choose to run your programs in the model
• But, state can be primitively implemented under the hood if you like

• for instance by C stack+heap

### Effectful Programs, Directly

• Can now program effectful computations directly:

let double () : ST unit = put (get () + get ())
• But, we want to prove properties about them too!

### Specifying Effectful Programs

• Choose an abstraction in which to reason about effectful programs

• E.g., A Hoare logic for stateful programs
• Encode the abstraction in an indexed monad

• E.g., A Hoare monad: STATE a (pre:state -> prop) (post:a -> state -> prop)
• Or, a Dijktra monad: STATE a (w: wp a)
• Rig F*'s effect system to infer a type for effectful programs in your chosen abstraction

### Weakest preconditions for stateful programs

• A WP predicate transformer w for a program e means
forall post h0.        //for all postconditions and initial states
w post h0 ==>       //given the weakest precondition is valid for post and h0
e h0 ~>* (v, h1) /\ //the computation reduces to a value and final state
post (v, h1)        //that validate the postcondition
• What is the type of a WP predicate transformer?

let st_wp a : st_post a -> st_pre  //transforms postconditions to preconditions
where st_post a : (a * s) -> prop //postconditions relate results to memories
and   st_pre : s -> prop //preconditions are memory predicates

### WP inference $\sim$ CPS tranform

let st_wp a : st_post a -> st_pre  //transforms postconditions to preconditions
where st_post a : (a * s) -> prop //postconditions relate results to memories
and   st_pre : s -> prop //preconditions are memory predicates
• Hey, wait a minute isn't that like the continuation monad?
cont r a = (a -> r) -> r
• Take the continuation monad with result type prop

s -> cont prop (a * s)
= s -> ((a * s) -> prop) -> prop
~ ((a * s) -> prop) -> s -> prop
= st_post a -> st_pre
= st_wp a
• WPs for state are just the state monad transformer applied to the continuation monad with result type prop

stateT m a = s -> m (a * s)
st_wp a = stateT (cont prop) a

### Deriving WP calculi for monadic effects

• You can define a weakest precondition calculus for m-effectful computations whose WPs have the form

m_wp a = mT (cont prop) a
• the m transformer on the continuation monad with result type prop
• And design an effect so that F* infers computations types of the form M a (wp : m_wp a)

• E.g., for exceptions

ex_wp a = ex_t (cont prop) a
= (option a -> prop) -> prop
• For state + exceptions

st_ex_wp a = state_t (ex_t (cont prop)) a
= s -> (ex_t (cont prop) a * s)
= s -> ((option a * state) -> prop) -> prop
~ ((option a * state) -> prop) -> s -> prop
= st_ex_post a -> st_ex_pre

### Derived Specifications: Hoare triples

• Reasoning about continuations: great for a core logic / not for a human

• Hoare logic-style pre-conditions and post-conditions

ST a (pre: s -> prop) (post: s -> a -> s -> prop) =
STATE a (fun k s0 -> pre s0 /\ (forall x s1. post s0 x s1 ==> k (x, s1)))
• stateful pre-condition is predicate on initial states

• post-condition is relation on initial states, results, and final states

val get ()
: ST s
(requires fun s -> True)
(ensures fun s0 result s1 -> s0 == result /\ result == s1)

val put s
: ST unit
(requires fun _ -> True)
(ensures fun _ _ s1 -> s1 == s0)

### Now we can prove double in our chosen abstraction

let double ()
: ST unit
(requires fun _ -> True)
(ensures fun s0 _ s1 -> s1 = 2*s0)
= put (get () + get ())

### Pick your abstraction for program reasoning

• For some applications, you may only be interested in enforcing certain security properties

• E.g., lattice-based information flow control

### A Primer on Information Flow Control

• Given a lattice of “security labels”:

• Labels: L

• lo, hi, etc.
• Sets of principals: {alice}, {bob}, {alice,bob},
• With some partial order <=

• And a least upper bound, lub, where l <= lub l m /\ m <= lub l m

• For a notion of program memories mem = loc -> value,

• And given a labelling of locations, e.g., label_of: loc -> label

• Define a label-indexed equivalence relation on memories:

m0 ~_l m1  = forall loc. label_of loc <= l ==> m0 loc == m1 loc

### Noninterference

• For a program P : mem -> mem,

• P is noninterferent at label l, if and only if

forall m0 m1. m0 ~_l m1 ==> P m0 ~_l P m1

i.e., P does not leak information from locations labeled greater than l to locations labeled l or below.