#![cfg_attr(feature = "cargo-clippy", allow(type_complexity))] // This is an implementation of the extended arithmetic server from // Vasconcelos-Gay-Ravara (2006) with some additional functionality extern crate session_types; use session_types::*; use std::thread::spawn; // Offers: Add, Negate, Sqrt, Eval type Srv = Offer< Eps, Offer< Recv>>>, Offer< Recv>>, Offer< Recv>, Var>>, Recv bool, Recv>>>, >, >, >, >; fn server(c: Chan<(), Rec>) { let mut c = c.enter(); loop { c = offer! { c, CLOSE => { c.close(); return }, ADD => { let (c, n) = c.recv(); let (c, m) = c.recv(); c.send(n + m).zero() }, NEGATE => { let (c, n) = c.recv(); c.send(-n).zero() }, SQRT => { let (c, x) = c.recv(); if x >= 0.0 { c.sel1().send(x.sqrt()).zero() } else { c.sel2().zero() } }, EVAL => { let (c, f) = c.recv(); let (c, n) = c.recv(); c.send(f(n)).zero() } } } } // `add_client`, `neg_client` and `sqrt_client` are all pretty straightforward // uses of session types, but they do showcase subtyping, recursion and how to // work the types in general. type AddCli = Choose>>>, R>>; fn add_client(c: Chan<(), Rec>>) { let (c, n) = c.enter().sel2().sel1().send(42).send(1).recv(); println!("{}", n); c.zero().sel1().close() } type NegCli = Choose>>, S>>>; fn neg_client(c: Chan<(), Rec>>) { let (c, n) = c.enter().skip2().sel1().send(42).recv(); println!("{}", n); c.zero().sel1().close(); } type SqrtCli = Choose>, Var>>, T>>>>; fn sqrt_client(c: Chan<(), Rec>>) { match c.enter().skip3().sel1().send(42.0).offer() { Left(c) => { let (c, n) = c.recv(); println!("{}", n); c.zero().sel1().close(); } Right(c) => { println!("Couldn't take square root!"); c.zero().sel1().close(); } } } // `fn_client` sends a function over the channel type PrimeCli = Choose< Eps, Choose bool, Send>>>>>>, >; fn fn_client(c: Chan<(), Rec>>) { fn even(n: i64) -> bool { n % 2 == 0 } let (c, b) = c.enter().skip4().send(even).send(42).recv(); println!("{}", b); c.zero().sel1().close(); } // `ask_neg` and `get_neg` use delegation, that is, sending a channel over // another channel. // `ask_neg` selects the negation operation and sends an integer, whereafter it // sends the whole channel to `get_neg`. `get_neg` then receives the negated // integer and prints it. type AskNeg = Choose>>, S>>>; fn ask_neg( c1: Chan<(), Rec>>, c2: Chan<(), Send, ()), Recv>>, Eps>>, ) { let c1 = c1.enter().sel2().sel2().sel1().send(42); c2.send(c1).close(); } fn get_neg( c1: Chan<(), Recv, ()), Recv>>, Eps>>, ) { let (c1, c2) = c1.recv(); let (c2, n) = c2.recv(); println!("{}", n); c2.zero().sel1().close(); c1.close(); } fn main() { connect(server, add_client); connect(server, neg_client); connect(server, sqrt_client); connect(server, fn_client); let (c1, c1_) = session_channel(); let (c2, c2_) = session_channel(); let t1 = spawn(move || server(c1)); let t2 = spawn(move || ask_neg(c1_, c2)); let t3 = spawn(move || get_neg(c2_)); let _ = t1.join(); let _ = t2.join(); let _ = t3.join(); }