functional reactive rust - lambda days · functional reactive programming frp — the basic idea...

40
Functional Reactive Rust Building applications with streams and signals using Rust & Carboxyl by Eduard Bopp at Lambda Days 2016 1 / 40

Upload: others

Post on 01-Oct-2020

11 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Functional Reactive Rust

Building applications with streams and signals using Rust & Carboxyl

by Eduard Bopp at Lambda Days 20161 / 40

Page 2: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Motivation1. Systems programming, trouble with event handling

2. Sold on FRP, but due to performance can't use Haskell, Clojure, Elixir, Elm…

2 / 40

Page 3: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

CarboxylFRP library written in safe Rust

Originally built for game development

On GitHub: aepsil0n/carboxyl

On crates.io: carboxyl 0.2

3 / 40

Page 4: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Rust

4 / 40

Page 5: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Rust — OverviewSystems programming language

Near C performance

Statically prevents segfaults

Guarantees thread-safety

Zero-cost abstractions

Lots of functional programming features

5 / 40

Page 6: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Rust — Hello, World!fn main() { println!("Hello, World!");}

6 / 40

Page 7: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Rust — Let BindingsImmutable

let x: i32 = 1;

Mutable

let mut y: i32 = 5;y = 4;println!("{}", y); // --> 4

7 / 40

Page 8: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Rust — Ownershipfn sum(v: Vec<i32>) -> i32 { v.into_iter() .fold(0, |s, x| s + x)}

let v = vec![1, 2, 3];let result = sum(v);

cannot use v any longer!

8 / 40

Page 9: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Rust — Borrowingfn sum(v: &[i32]) -> i32 { v.iter() .fold(0, |s, x| s + x)}

let v = vec![1, 2, 3];let result = sum(&v);

9 / 40

Page 10: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Rust — Mutable borrowslet mut x = 5;{ let y = &mut x; *y += 1;}println!("{}", x);

10 / 40

Page 11: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Rust — Structs & Enumsstruct Point { x: f64, y: f64,}

enum Option<T> { Some(T), None}

11 / 40

Page 12: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Functional Reactive Programming

12 / 40

Page 13: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

FRP — The basic ideaFunctional approach to handle how events affect application state

map, filter, fold, etc. over time-varying data structures

Comes in a million different flavours

13 / 40

Page 14: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Carboxyl's flavour of FRPTwo types:

Stream is a sequence of discrete events

Signal is a value that varies continuously over time

Time is implicit via transactions

14 / 40

Page 15: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Overview

15 / 40

Page 16: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Building streams & signalsextern crate carboxyl;use carboxyl::{Sink, Stream, Signal};

let sink = Sink::new();let stream = sink.stream();let signal = stream.hold(3);

assert_eq!(signal.sample(), 3);

sink.send(5);assert_eq!(signal.sample(), 5);

16 / 40

Page 17: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Iterate over streamlet sink = Sink::new();let stream = sink.stream();

let mut events = stream.events();sink.send(4);assert_eq!(events.next(), Some(4));

17 / 40

Page 18: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Mapstream.map(|x| x * x)

18 / 40

Page 19: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Filterstream.filter(|&x| x < 0)stream.filter_map(|&x| if x > 2 { Some(x - 2) } else { None })option_stream.filter_some()

19 / 40

Page 20: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Mergestream_a.merge(&stream_b)

20 / 40

Page 21: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Foldstream().fold(0, |a, b| a + b)

21 / 40

Page 22: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Snapshotfn func(t: Time, value: Event) -> NewThing { /* ... */ }

let time: Signal<Time> = ...;time.snapshot(&stream, func)

22 / 40

Page 23: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Liftfn f(a: A, b: B) -> C { /* ... */ }

lift!(f, &as, &bs)

Only for up to arity 4, because of macros…

23 / 40

Page 24: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

MoreDynamic switching of streams and signals

Coalesce to resolve events from the same transaction

24 / 40

Page 25: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Building applications

25 / 40

Page 26: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Crate ecosystemcarboxyl-xyz for command line interface, system time, windowing

Elmesque: port of Elm graphics API to Rust

Piston: modular game engine

Gfx, Glium: 3D graphics

Glutin: windowing context

lots more…

26 / 40

Page 27: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Demo time!

27 / 40

Page 28: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Application structurefn app<W: StreamingWindow>(window: &W) -> Signal<View> { let context = context(window); let actions = context .snapshot(&events(window), intent) .filter_some(); let state = actions.fold(init(), update); lift!(view, &context, &state)}

adapted from Cycle.js & Elm architecture for continuous time semantics

28 / 40

Page 29: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Contextsignal part of the input

#[derive(Clone)]enum Context { Hover, Free }

fn centered(size: Dimension, position: Position) -> Position { /* ... */ }

fn hovers(position: Position) -> bool { /* ... */ }

fn context<W: StreamingWindow>(window: &W) -> Signal<Context> { lift!( |size, cursor| if hovers(centered(size, cursor)) { Context::Hover } else { Context::Free }, &window.size(), &window.cursor() )}

29 / 40

Page 30: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Eventsdiscrete part of the input

#[derive(Clone)]enum Event { Click }

fn clicks(event: ButtonEvent) -> Option<Event> { /* ... */ }

fn events<W: StreamingWindow>(window: &W) -> Stream<Event> { window.buttons() .filter_map(clicks)}

30 / 40

Page 31: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Actions#[derive(Clone)]enum Action { Toggle }

fn intent(context: Context, _: Event) -> Option<Action> { match context { Context::Hover => Some(Action::Toggle), Context::Free => None }}

let actions = context .snapshot(&events(window), intent) .filter_some();

31 / 40

Page 32: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Statetype State = bool;

fn init() -> bool { false }

fn update(current: State, _: Action) -> State { !current }

let state = actions.fold(init(), update);

32 / 40

Page 33: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Viewtype View = Vec<Form>;

fn hello() -> Form { /* ... */ }fn button(color: Color) -> Form { /* ... */ }

fn view(context: Context, state: State) -> View { let color = match context { Context::Hover => if state { light_blue() } else { light_orange() }, Context::Free => if state { blue() } else { orange() } }; vec![button(color), hello()]}

let output = lift!(view, &context, &state);

33 / 40

Page 34: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

More…Composition

Effects

34 / 40

Page 35: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Implementation

35 / 40

Page 36: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

InspirationsOriginally similar to Sodium

Later looked at Push-Pull FRP by C. Elliott

But: Purely functional approach is not feasible with strict evaluation and lifetimes

36 / 40

Page 37: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Implementation strategyUse observer pattern internally

Make discrete changes atomic using transactions

Signals are impure functions (system time, analog instruments, etc.)

37 / 40

Page 38: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Current pain pointsLots of atomic reference counting

Lots of heap allocation and pointer indirection

Transactions are pretty dumb

Global mutex prevents parallel event processing

38 / 40

Page 39: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Ressources

Rust

https://users.rust-lang.org/

https://www.reddit.com/r/rust/

Carboxyl

https://crates.io/crates/carboxyl

https://github.com/aepsil0n/carboxyl

39 / 40

Page 40: Functional Reactive Rust - Lambda Days · Functional Reactive Programming FRP — The basic idea Functional approach to handle how events affect application state map, filter, fold,

Thank you!

Twitter: @aepsil0n

Email: eduard.bopp (at) aepsil0n.de

40 / 40