Data Driven UIs, Incrementally
Yaron Minsky / Jane Street Group
Your UI as two functions
Action
Model
Vdom
module type Component = sig
type model
type action
val apply_action : model -> action -> model
val render : model -> Vdom.t
endYour UI as an Incremental Computation
Model
Vdom
DOM
Model'
Vdom'
DOM'
Incrementality with Incremental
- Your computation as a dependency graph!
- Based on Acar's Self Adjusting Computations
Map and Map2
Model
Sub 1
Sub 2
Vdom 1
Vdom 2
Vdom
val map : 'a Incr.t -> ('a -> 'b) -> 'b Incr.tval map2 :
'a Incr.t -> 'b Incr.t -> ('a -> 'b -> 'c) -> 'c Incr.tBind
Model
Sub 1
Sub 2
Vdom 1
Vdom 2
Vdom
val bind : 'a Incr.t -> ('a -> 'b Incr.t) -> 'b Incr.tIncremental thus far
- Map works for static structures
- Bind adds (limited) dynamism
- But how can we be dynamic and incremental?
Mapping over an incremental map
val incr_map
: ('k, 'v1, 'cmp) Map.t Incr.t
-> ('v1 -> 'v2)
-> ('k, 'v2, 'cmp) Map.t Incr.tm
let incr_map m f =
let%map m = m in
Map.map m fm'
Diffing Maps
val symmetric_diff
: ('k, 'v, 'cmp) Map.t
-> ('k, 'v, 'cmp) Map.t
-> data_equal:('v -> 'v -> bool)
-> ('k * [ `Left of 'v
| `Right of 'v
| `Unequal of 'v * 'v ]) Sequence.tHooking in Diffs with diff_map
val diff_map
: 'a Incr.t
-> (('a * 'b) option -> 'a -> 'b)
-> 'b Incr.tlet diff_map i f =
let old = ref None in
let%map a = i in
let b = f !old a in
old := Some (a, b);
bImplementing incr_map
let incr_map m f =
diff_map m (fun old m ->
match old with
| None -> Map.map m ~f
| Some (old_in, old_out) ->
let diff =
Map.symmetric_diff old_in m
~data_equal:phys_equal
in
Sequence.fold diff ~init:old_out
~f:(fun acc (key,change) ->
match change with
| `Left _ -> Map.remove acc key
| `Unequal (_,data) | `Right data ->
Map.set acc ~key ~data:(f data)))Mapping an incremental function over an incremental map
val incr_map'
: ('k, 'v1, 'cmp) Map.t Incr.t
-> ('v1 Incr.t -> 'v2 Incr.t)
-> ('k, 'v2, 'cmp) Map.t Incr.tMapping an incremental function over an incremental map
m
e
e
e
e
e'
e'
e'
e'
m'
Split and Join
val split
: ('k,'v ,'cmp) Map.t Incr.t
-> ('k,'v Incr.t,'cmp) Map.t Incr.tval join
: ('k,'v Incr.t,'cmp) Map.t Incr.t
-> ('k,'v ,'cmp) Map.t Incr.tlet incr_map' m f =
join (incr_map (split m) f)Extending Incremental
- diff_map works on any diffable data structure
- Incremental.Expert gives a low-level interface
- Incr_map for Map-specific primitives
- Incr_select for range and focus operations
Beyond the browser
Server
Client
Full
model
Client
view
Model
Vdom
DOM
Full
model
Client
view
Model
Vdom
DOM
Things to remember
- UI design is an optimization problem
- SAC is a powerful optimization tool
- Diff and Patch are incremental functional glue
https://github.com/janestreet/incr_dom
https://opensource.janestreet.com
Join us!
http://janestreet.com/apply
Interviewing for co-op/internship and full-time positions
Data Driven UIs, Incrementally (Campus Talk)
By Yaron Minsky
Data Driven UIs, Incrementally (Campus Talk)
- 404