Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

rewritten talk + examples

  • Loading branch information...
commit 36f440dc5205a9fb01b42843f8b910b4adf7545f 1 parent df2c2b4
Jake Donham authored
View
2  examples/froc-dom/talk/_tags
@@ -1,2 +1,2 @@
-<talk.ml> : pkg_froc-dom,pkg_javascript,syntax_camlp4o,pkg_camlp4.macro
+<talk.ml> : syntax_camlp4o,pkg_jslib.inline,pkg_ocamljs,pkg_froc-dom,pkg_javascript,syntax_camlp4o,pkg_camlp4.macro
<talk.js> : pkg_froc-dom,pkg_javascript
View
324 examples/froc-dom/talk/index.html
@@ -8,73 +8,81 @@
<div id="slides">
<div id="title" class="center">
- <h1 id="title_title"></h1>
+ <h1 id="title_title">Better Web GUIs<br/>with<br/>Functional Reactive Programming</h1>
<h2 id="title_email">jake@donham.org</h2>
</div>
- <div id="reactive" class="slide" style="display:none">
- <h2>What is reactive programming?</h2>
+ <div id="gui_hard" class="slide" style="display:none">
+ <h2>GUIs are hard!</h2>
+
+ <p>Typical GUI impl consists of</p>
<ul>
- <li>program is in some state</li>
- <li>s**t happens (timer goes off, input arrives)</li>
- <li>program reacts, changing state (and usually responding with output)</li>
+ <li>state</li>
+ <li>events</li>
+ <li>callbacks</li>
</ul>
- <p>e.g.</p>
-
+ <p>Problems:</p>
<ul>
- <li>GUIs</li>
- <li>network stacks</li>
- <li>industrial control systems</li>
+ <li>unmodular: callback soup, control inversion</li>
+ <li>state + concurrency = pain (order matters)</li>
</ul>
- <p>most programming is reactive programming (?)</p>
+ <p>Adobe: 1/3 code and 1/2 bugs due to event handling</p>
</div>
- <div id="gui" class="slide" style="display:none">
- <h2>GUI as reactive system</h2>
+ <div id="gui_not_hard" class="slide" style="display:none">
+ <h2>GUIs should not be hard!</h2>
+
+ <p>Typical GUI represents a <em>thing</em></p>
<ul>
- <li>state of the GUI (and internal state)</li>
- <li>clicks happen</li>
- <li>screen is updated (and internal state, and output)</li>
+ <li>internal model of the thing</li>
+ <li>input events control model</li>
+ <li>render view of thing from model</li>
</ul>
- <p>GUIs are hard!</p>
+ <p>Elements of functional programming:</p>
<ul>
- <li>lots of state involved</li>
- <li>inherently concurrent (e.g. rendering a web page)</li>
- <li>a painful combination...</li>
+ <li>view is a pure function of model
+ <li>model is a pure function of events?
</ul>
</div>
<div id="mvc" class="slide" style="display:none">
- <h2>Model-view-controller</h2>
+ <h2>Model-view-controller pattern</h2>
<ul>
- <li>separate the state of the GUI (<em>view</em>) from internal state (<em>model</em>)
- <li>updates happen on model, view is derived (i.e. a function!)
- <li>view registers <em>listener</em> with model, to be notified of changes
+ <li>separate modules/classes for model, view, controller</li>
+ <li>view registers callbacks with model, controller fires events on model</li>
</ul>
- <p>Better than nothing, but:</p>
+ <p>Better, but:</p>
<ul>
- <li>listener spaghetti</li>
<li>no dependency ordering leads to update glitches</li>
<li>programmer must manage dynamic dependencies</li>
+ <li>still: callback soup, state + concurrency</li>
+ </ul>
+
+ <p>And:</p>
+ <ul>
+ <li>still too concrete / coupled (want MMM...MMMVC?)</li>
<li>high programmer overhead leads to shortcuts</li>
- <li>inherently state-based</li>
</ul>
</div>
<div id="frp" class="slide" style="display:none">
<h2>Functional reactive programming</h2>
+ <p>Slogan:<p>
+ <p style="margin-left: 250px"><b>Dependencies not callbacks!</b></p>
+ <br/>
+
<p>a more abstract take on MVC:
<ul>
- <li>don't register a listener, depend on a value</li>
+ <li>functional dependencies replace callbacks and state</li>
<li>framework updates dependencies in glitch-free order</li>
<li>framework manages dynamic dependencies</li>
- <li>low programmer overhead</li>
+ <li>abstract, decoupled, low programmer overhead</li>
<li>implementation is state-based, interface is (mostly) pure</li>
</ul>
</div>
@@ -82,10 +90,34 @@ <h2 id="title_email">jake@donham.org</h2>
<div id="ocaml_etc" class="slide" style="display:none">
<h2>OCaml, ocamljs, froc, froc-dom</h2>
<ul>
- <li>OCaml is a programming language</li>
- <li>ocamljs compiles OCaml to Javascript</li>
- <li>froc is an FRP library written in OCaml</li>
- <li>froc-dom connects froc to browsers</li>
+ <li>OCaml: a programming language</li>
+ <li>ocamljs: compiles OCaml to Javascript</li>
+ <li>froc: functional reactive programming library (in OCaml)</li>
+ <li>froc-dom: connects froc to browser DOM</li>
+ </ul>
+ </div>
+
+ <div id="frp_behaviors" class="slide" style="display:none">
+ <h2>FRP behaviors</h2>
+ <ul>
+ <li>first-class changeable values, separate from source:
+ <pre>
+<span class="tuareg-font-lock-governing">let</span> <span class="variable-name">input </span><span class="tuareg-font-lock-operator">=</span> <span class="type">Dom</span>.document<span class="tuareg-font-lock-operator">#</span>getElementById <span class="string">"input"</span> <span class="tuareg-font-lock-governing">in</span>
+<span class="tuareg-font-lock-governing">let</span> <span class="variable-name">value </span><span class="tuareg-font-lock-operator">=</span> <span class="type">Froc_dom</span>.input_value_b input <span class="tuareg-font-lock-governing">in</span> <span class="tuareg-font-lock-operator">...</span>
+</pre>
+ </li>
+ <li>derived behaviors are recomputed when dependencies change:
+ <pre>
+<span class="tuareg-font-lock-governing">let</span> <span class="variable-name">int_value </span><span class="tuareg-font-lock-operator">=</span> <span class="type">Froc</span>.lift int_of_string value <span class="tuareg-font-lock-governing">in</span>
+<span class="tuareg-font-lock-governing">let</span> <span class="variable-name">double_value </span><span class="tuareg-font-lock-operator">=</span> <span class="type">Froc</span>.lift <span class="tuareg-font-lock-operator">(</span><span class="keyword">fun</span> <span class="variable-name">x </span><span class="tuareg-font-lock-operator">-&gt;</span> x <span class="tuareg-font-lock-operator">+</span> x<span class="tuareg-font-lock-operator">)</span> int_value
+</pre>
+ </li>
+ <li>glitch-free update:
+ <pre>
+<span class="tuareg-font-lock-governing">let</span> <span class="variable-name">triple_value </span><span class="tuareg-font-lock-operator">=</span>
+ <span class="type">Froc</span>.lift2 <span class="tuareg-font-lock-operator">(</span><span class="keyword">fun</span> <span class="variable-name">x y </span><span class="tuareg-font-lock-operator">-&gt;</span> x <span class="tuareg-font-lock-operator">+</span> y<span class="tuareg-font-lock-operator">)</span> int_value double_value
+</pre>
+ </li>
</ul>
</div>
@@ -108,64 +140,186 @@ <h2 id="title_email">jake@donham.org</h2>
<span class="tuareg-font-lock-governing">let</span> <span class="variable-name">num_clicks </span><span class="tuareg-font-lock-operator">=</span> <span class="type">Froc</span>.collect <span class="tuareg-font-lock-operator">(</span><span class="keyword">fun</span> <span class="variable-name">c _ </span><span class="tuareg-font-lock-operator">-&gt;</span> c <span class="tuareg-font-lock-operator">+</span> 1<span class="tuareg-font-lock-operator">)</span> 0 clicks
</pre>
</li>
- </ul>
- </div>
-
- <div id="frp_behaviors" class="slide" style="display:none">
- <h2>FRP behaviors</h2>
- <ul>
- <li>first-class changeable values, separate from source:
- <pre>
-<span class="tuareg-font-lock-governing">let</span> <span class="variable-name">input </span><span class="tuareg-font-lock-operator">=</span> <span class="type">Dom</span>.document<span class="tuareg-font-lock-operator">#</span>getElementById <span class="string">"input"</span> <span class="tuareg-font-lock-governing">in</span>
-<span class="tuareg-font-lock-governing">let</span> <span class="variable-name">value </span><span class="tuareg-font-lock-operator">=</span> <span class="type">Froc_dom</span>.input_value_b input <span class="tuareg-font-lock-governing">in</span> <span class="tuareg-font-lock-operator">...</span>
-</pre>
- </li>
- <li>dependents are recomputed when behavior changes:
- <pre>
-<span class="tuareg-font-lock-governing">let</span> <span class="variable-name">int_value </span><span class="tuareg-font-lock-operator">=</span> <span class="type">Froc</span>.lift int_of_string value <span class="tuareg-font-lock-governing">in</span>
-<span class="tuareg-font-lock-governing">let</span> <span class="variable-name">double_value </span><span class="tuareg-font-lock-operator">=</span> <span class="type">Froc</span>.lift <span class="tuareg-font-lock-operator">(</span><span class="keyword">fun</span> <span class="variable-name">x </span><span class="tuareg-font-lock-operator">-&gt;</span> x <span class="tuareg-font-lock-operator">+</span> x<span class="tuareg-font-lock-operator">)</span> int_value
-</pre>
- </li>
- <li>glitch-free update:
+ <li>make event from behavior and behavior from event
<pre>
-<span class="tuareg-font-lock-governing">let</span> <span class="variable-name">triple_value </span><span class="tuareg-font-lock-operator">=</span>
- <span class="type">Froc</span>.lift2 <span class="tuareg-font-lock-operator">(</span><span class="keyword">fun</span> <span class="variable-name">x y </span><span class="tuareg-font-lock-operator">-&gt;</span> x <span class="tuareg-font-lock-operator">+</span> y<span class="tuareg-font-lock-operator">)</span> int_value double_value
+<span class="tuareg-font-lock-governing">let</span> <span class="variable-name">e </span><span class="tuareg-font-lock-operator">=</span> <span class="type">Froc</span>.changes b <span class="tuareg-font-lock-governing">in</span> <span class="tuareg-font-lock-operator">...</span>
+<span class="tuareg-font-lock-governing">let</span> <span class="variable-name">e </span><span class="tuareg-font-lock-operator">=</span> <span class="type">Froc</span>.when_true b <span class="tuareg-font-lock-governing">in</span> <span class="tuareg-font-lock-operator">...</span>
+<span class="tuareg-font-lock-governing">let</span> <span class="variable-name">b </span><span class="tuareg-font-lock-operator">=</span> <span class="type">Froc</span>.hold e init <span class="tuareg-font-lock-governing">in</span> <span class="tuareg-font-lock-operator">...</span>
</pre>
</li>
</ul>
</div>
- <div id="dyn_deps" class="slide" style="display:none">
- <h2>Dynamic dependencies</h2>
- </div>
-
<div id="output" class="slide" style="display:none">
- <h2>Producing output</h2>
+ <h2>Notifications</h2>
<p>To do something with an event or behavior:</p>
<pre>
<span class="type">Froc</span>.notify_b fontSize <span class="tuareg-font-lock-operator">(</span><span class="keyword">fun</span> <span class="variable-name">s </span><span class="tuareg-font-lock-operator">-&gt;</span> e<span class="tuareg-font-lock-operator">#</span>_get_style<span class="tuareg-font-lock-operator">#</span>_set_fontSize s<span class="tuareg-font-lock-operator">);</span>
-
-<span class="type">Froc_dom</span>.attach_fontSize_b e fontSize<span class="tuareg-font-lock-operator">;</span>
</pre>
- <p>"Hey, wait, that's just a listener!"</p>
+ <p>"Hey, wait, that's just a callback!"</p>
<p>Yes, but:</p>
<ul>
<li>you only need them at the edge of the graph</li>
<li>dynamic dependencies are handled for you</li>
- <li>alternative: build a value representing the UI, draw it all at once</li>
+ <li>alternative: build a value representing whole UI, draw it on changes</li>
<li>at the end of the day you have to side-effect the screen
</ul>
+ </div>
+
+ <div id="dyn_deps" class="slide" style="display:none">
+ <h2>Dynamic dependencies</h2>
+
+ <ul>
+ <li>control flow depends on values</li>
+ <li>computation of values depends on control</li>
+ </ul>
+
+ <p>For example:</p>
+ <pre>
+<span class="comment">(* let y = if x = 0 then 0 else 100 / x *)</span>
+
+<span class="tuareg-font-lock-governing">let</span> <span class="variable-name">x </span><span class="tuareg-font-lock-operator">=</span> <span class="tuareg-font-lock-operator">...</span> <span class="tuareg-font-lock-governing">in</span>
+<span class="tuareg-font-lock-governing">let</span> <span class="variable-name">b </span><span class="tuareg-font-lock-operator">=</span> <span class="type">Froc</span>.lift <span class="tuareg-font-lock-operator">(</span><span class="keyword">fun</span> <span class="variable-name">x </span><span class="tuareg-font-lock-operator">-&gt;</span> x <span class="tuareg-font-lock-operator">=</span> 0<span class="tuareg-font-lock-operator">)</span> x <span class="tuareg-font-lock-governing">in</span>
+<span class="tuareg-font-lock-governing">let</span> <span class="variable-name">y </span><span class="tuareg-font-lock-operator">=</span> <span class="type">Froc</span>.bind b <span class="tuareg-font-lock-operator">(</span><span class="keyword">fun</span> <span class="variable-name">b </span><span class="tuareg-font-lock-operator">-&gt;</span>
+ <span class="keyword">if</span> b
+ <span class="keyword">then</span> <span class="type">Froc</span>.return 0
+ <span class="keyword">else</span> <span class="type">Froc</span>.lift <span class="tuareg-font-lock-operator">(</span><span class="keyword">fun</span> <span class="variable-name">x </span><span class="tuareg-font-lock-operator">-&gt;</span> 100 <span class="tuareg-font-lock-operator">/</span> x<span class="tuareg-font-lock-operator">)</span> x<span class="tuareg-font-lock-operator">)</span> <span class="tuareg-font-lock-governing">in</span> <span class="tuareg-font-lock-operator">...</span>
+</pre>
+ <p>Types:</p>
+<pre>
+<span class="tuareg-font-lock-governing">val</span> <span class="variable-name">lift </span><span class="tuareg-font-lock-operator">:</span> <span class="tuareg-font-lock-operator">('</span><span class="type">a </span><span class="tuareg-font-lock-operator">-&gt;</span><span class="type"> </span><span class="tuareg-font-lock-operator">'</span><span class="type">b</span><span class="tuareg-font-lock-operator">)</span><span class="type"> </span><span class="tuareg-font-lock-operator">-&gt;</span><span class="type"> </span><span class="tuareg-font-lock-operator">'</span><span class="type">a behavior </span><span class="tuareg-font-lock-operator">-&gt;</span><span class="type"> </span><span class="tuareg-font-lock-operator">'</span><span class="type">b behavior</span>
+<span class="tuareg-font-lock-governing">val</span> <span class="variable-name">bind </span><span class="tuareg-font-lock-operator">:</span> <span class="tuareg-font-lock-operator">'</span><span class="type">a behavior </span><span class="tuareg-font-lock-operator">-&gt;</span><span class="type"> </span><span class="tuareg-font-lock-operator">('</span><span class="type">a </span><span class="tuareg-font-lock-operator">-&gt;</span><span class="type"> </span><span class="tuareg-font-lock-operator">'</span><span class="type">b behavior</span><span class="tuareg-font-lock-operator">)</span><span class="type"> </span><span class="tuareg-font-lock-operator">-&gt;</span><span class="type"> </span><span class="tuareg-font-lock-operator">'</span><span class="type">b behavior</span>
+<span class="tuareg-font-lock-governing">val</span> <span class="variable-name">return </span><span class="tuareg-font-lock-operator">:</span> <span class="tuareg-font-lock-operator">'</span><span class="type">a </span><span class="tuareg-font-lock-operator">-&gt;</span><span class="type"> </span><span class="tuareg-font-lock-operator">'</span><span class="type">a behavior</span>
+</pre>
+ </div>
+
+ <div id="clicks" class="slide" style="display:none">
+ <h2>Example: Clicks</h2>
+
+ <p><button type="button" id="clicks_click">Click</button></p>
+ <p><span id="clicks_seconds">0</span> seconds have elapsed.</p>
+ <p>The button has been clicked <span id="clicks_clicks">0</span> times.</p>
+ <p><span id="clicks_difference"></span></p>
+ </div>
+
+ <div id="clicks_src" class="slide" style="display:none">
+ <h2>Example: Clicks source</h2>
+
+ <pre>
+<span class="tuareg-font-lock-governing">let</span> <span class="function-name">elem</span><span class="variable-name"> id </span><span class="tuareg-font-lock-operator">=</span> <span class="type">D</span>.document<span class="tuareg-font-lock-operator">#</span>getElementById id <span class="tuareg-font-lock-governing">in</span>
+
+<span class="tuareg-font-lock-governing">let</span> <span class="variable-name">clicks </span><span class="tuareg-font-lock-operator">=</span> <span class="type">F</span>.count <span class="tuareg-font-lock-operator">(</span><span class="type">Fd</span>.clicks <span class="tuareg-font-lock-operator">(</span>elem <span class="string">"click"</span><span class="tuareg-font-lock-operator">))</span> <span class="tuareg-font-lock-governing">in</span>
+
+<span class="tuareg-font-lock-governing">let</span> <span class="variable-name">ticks </span><span class="tuareg-font-lock-operator">=</span> <span class="type">F</span>.count <span class="tuareg-font-lock-operator">(</span><span class="type">Fd</span>.ticks 1000.<span class="tuareg-font-lock-operator">)</span> <span class="tuareg-font-lock-governing">in</span>
+
+<span class="type">Fd</span>.attach_innerHTML_b <span class="tuareg-font-lock-operator">(</span>elem <span class="string">"clicks"</span><span class="tuareg-font-lock-operator">)</span> <span class="tuareg-font-lock-operator">(</span><span class="type">F</span>.lift string_of_int clicks<span class="tuareg-font-lock-operator">);</span>
+
+<span class="type">Fd</span>.attach_innerHTML_b <span class="tuareg-font-lock-operator">(</span>elem <span class="string">"seconds"</span><span class="tuareg-font-lock-operator">)</span> <span class="tuareg-font-lock-operator">(</span><span class="type">F</span>.lift string_of_int ticks<span class="tuareg-font-lock-operator">);</span>
+
+<span class="type">Fd</span>.attach_innerHTML_b <span class="tuareg-font-lock-operator">(</span>elem <span class="string">"difference"</span><span class="tuareg-font-lock-operator">)</span>
+ <span class="tuareg-font-lock-operator">(</span><span class="type">F</span>.lift2
+ <span class="tuareg-font-lock-operator">(</span><span class="keyword">fun</span> <span class="variable-name">clicks ticks </span><span class="tuareg-font-lock-operator">-&gt;</span>
+ <span class="keyword">if</span> clicks <span class="tuareg-font-lock-operator">=</span> ticks
+ <span class="keyword">then</span> <span class="string">"same number of clicks as ticks"</span>
+ <span class="keyword">else</span> <span class="keyword">if</span> clicks <span class="tuareg-font-lock-operator">&gt;</span> ticks
+ <span class="keyword">then</span> string_of_int <span class="tuareg-font-lock-operator">(</span>clicks <span class="tuareg-font-lock-operator">-</span> ticks<span class="tuareg-font-lock-operator">)</span> <span class="tuareg-font-lock-operator">^</span> <span class="string">" more clicks than ticks"</span>
+ <span class="keyword">else</span> string_of_int <span class="tuareg-font-lock-operator">(</span>ticks <span class="tuareg-font-lock-operator">-</span> clicks<span class="tuareg-font-lock-operator">)</span> <span class="tuareg-font-lock-operator">^</span> <span class="string">" more ticks than clicks"</span><span class="tuareg-font-lock-operator">)</span>
+ clicks ticks<span class="tuareg-font-lock-operator">)</span>
+</pre>
+ </div>
+
+ <div id="sudoku" class="slide" style="display:none">
+ <h2>Example: Sudoku</h2>
+ <div id="sudoku_board"></div>
+ </div>
+
+ <div id="sudoku_src" class="slide" style="display:none">
+ <h2>Example: Sudoku source</h2>
+
+ <pre>
+<span class="tuareg-font-lock-governing">let</span> <span class="function-name">adjacents</span><span class="variable-name"> i j </span><span class="tuareg-font-lock-operator">=</span>
+ <span class="tuareg-font-lock-governing">let</span> <span class="function-name">adj</span><span class="variable-name"> i</span><span class="tuareg-font-lock-operator">'</span><span class="variable-name"> j</span><span class="tuareg-font-lock-operator">'</span><span class="variable-name"> </span><span class="tuareg-font-lock-operator">=</span>
+ <span class="tuareg-font-lock-operator">(</span>i<span class="tuareg-font-lock-operator">'</span> <span class="tuareg-font-lock-operator">&lt;&gt;</span> i <span class="tuareg-font-lock-operator">||</span> j<span class="tuareg-font-lock-operator">'</span> <span class="tuareg-font-lock-operator">&lt;&gt;</span> j<span class="tuareg-font-lock-operator">)</span> <span class="tuareg-font-lock-operator">&amp;&amp;</span>
+ <span class="tuareg-font-lock-operator">(</span>i<span class="tuareg-font-lock-operator">'</span> <span class="tuareg-font-lock-operator">=</span> i <span class="tuareg-font-lock-operator">or</span> j<span class="tuareg-font-lock-operator">'</span> <span class="tuareg-font-lock-operator">=</span> j <span class="tuareg-font-lock-operator">or</span>
+ <span class="tuareg-font-lock-operator">(</span>i<span class="tuareg-font-lock-operator">'</span> <span class="tuareg-font-lock-operator">/</span> 3 <span class="tuareg-font-lock-operator">=</span> i <span class="tuareg-font-lock-operator">/</span> 3 <span class="tuareg-font-lock-operator">&amp;&amp;</span> j<span class="tuareg-font-lock-operator">'</span> <span class="tuareg-font-lock-operator">/</span> 3 <span class="tuareg-font-lock-operator">=</span> j <span class="tuareg-font-lock-operator">/</span> 3<span class="tuareg-font-lock-operator">))</span> <span class="tuareg-font-lock-governing">in</span>
+ foldi <span class="tuareg-font-lock-operator">[]</span> rows <span class="tuareg-font-lock-governing">begin</span> <span class="keyword">fun</span> <span class="variable-name">i</span><span class="tuareg-font-lock-operator">'</span><span class="variable-name"> adjs row </span><span class="tuareg-font-lock-operator">-&gt;</span>
+ foldi adjs row <span class="tuareg-font-lock-governing">begin</span> <span class="keyword">fun</span> <span class="variable-name">j</span><span class="tuareg-font-lock-operator">'</span><span class="variable-name"> adjs </span><span class="tuareg-font-lock-operator">(</span><span class="variable-name">cell</span><span class="tuareg-font-lock-operator">,</span><span class="variable-name">_</span><span class="tuareg-font-lock-operator">,</span><span class="variable-name">_</span><span class="tuareg-font-lock-operator">)</span><span class="variable-name"> </span><span class="tuareg-font-lock-operator">-&gt;</span>
+ <span class="keyword">if</span> adj i<span class="tuareg-font-lock-operator">'</span> j<span class="tuareg-font-lock-operator">'</span>
+ <span class="keyword">then</span> cell<span class="tuareg-font-lock-operator">::</span>adjs
+ <span class="keyword">else</span> adjs
+ <span class="tuareg-font-lock-governing">end</span>
+ <span class="tuareg-font-lock-governing">end</span>
+
+<span class="type">ArrayLabels</span>.iteri rows <span class="tuareg-font-lock-operator">~</span><span class="variable-name">f</span><span class="tuareg-font-lock-operator">:</span><span class="tuareg-font-lock-governing">begin</span><span class="type"> </span><span class="keyword">fun</span><span class="type"> </span><span class="variable-name">i row </span><span class="tuareg-font-lock-operator">-&gt;</span>
+ <span class="type">ArrayLabels</span>.iteri row <span class="tuareg-font-lock-operator">~</span><span class="variable-name">f</span><span class="tuareg-font-lock-operator">:</span><span class="tuareg-font-lock-governing">begin</span><span class="type"> </span><span class="keyword">fun</span><span class="type"> </span><span class="variable-name">j </span><span class="tuareg-font-lock-operator">(</span><span class="variable-name">cell</span><span class="tuareg-font-lock-operator">,</span><span class="variable-name"> _</span><span class="tuareg-font-lock-operator">,</span><span class="variable-name"> input</span><span class="tuareg-font-lock-operator">)</span><span class="variable-name"> </span><span class="tuareg-font-lock-operator">-&gt;</span>
+ <span class="tuareg-font-lock-governing">let</span> <span class="variable-name">adjs </span><span class="tuareg-font-lock-operator">=</span> adjacents i j <span class="tuareg-font-lock-governing">in</span>
+ <span class="type">Fd</span>.attach_backgroundColor_b input
+ <span class="tuareg-font-lock-operator">(</span><span class="type">F</span>.bindN adjs <span class="tuareg-font-lock-operator">(</span><span class="keyword">fun</span> <span class="variable-name">adjs </span><span class="tuareg-font-lock-operator">-&gt;</span>
+ <span class="type">F</span>.lift
+ <span class="tuareg-font-lock-operator">(</span><span class="keyword">fun</span> <span class="variable-name">v </span><span class="tuareg-font-lock-operator">-&gt;</span>
+ <span class="keyword">if</span> v <span class="tuareg-font-lock-operator">&lt;&gt;</span> None <span class="tuareg-font-lock-operator">&amp;&amp;</span> <span class="type">List</span>.mem v adjs
+ <span class="keyword">then</span> <span class="string">"#ff0000"</span>
+ <span class="keyword">else</span> <span class="string">"#ffffff"</span><span class="tuareg-font-lock-operator">)</span>
+ cell<span class="tuareg-font-lock-operator">))</span>
+ <span class="tuareg-font-lock-governing">end</span>
+<span class="tuareg-font-lock-governing">end</span>
+</pre>
+ </div>
+
+ <div id="bounce" class="slide" style="display:none">
+ <h2>Example: Bounce</h2>
+ <canvas id="bounce_canvas" width="500" height="500" style="border-style:solid"></canvas>
+ </div>
+
+ <div id="bounce_src" class="slide" style="display:none">
+ <h2>Example: Bounce source</h2>
+
+ <pre style="font-size: 16px">
+<span class="tuareg-font-lock-governing">let</span> <span class="variable-name">ball_point </span><span class="tuareg-font-lock-operator">=</span>
+ <span class="type">F</span>.fix_b <span class="tuareg-font-lock-governing">begin</span> <span class="keyword">fun</span> <span class="variable-name">bp </span><span class="tuareg-font-lock-operator">-&gt;</span>
+ <span class="tuareg-font-lock-governing">let</span> <span class="variable-name">x_out_of_bounds </span><span class="tuareg-font-lock-operator">=</span>
+ <span class="type">F</span>.map
+ <span class="tuareg-font-lock-operator">(</span><span class="keyword">fun</span> <span class="tuareg-font-lock-operator">()</span><span class="variable-name"> </span><span class="tuareg-font-lock-operator">-&gt;</span> `X_bounds<span class="tuareg-font-lock-operator">)</span>
+ <span class="tuareg-font-lock-operator">(</span><span class="type">F</span>.when_true
+ <span class="tuareg-font-lock-operator">(</span><span class="type">F</span>.lift <span class="tuareg-font-lock-operator">(</span><span class="keyword">fun</span> <span class="tuareg-font-lock-operator">(</span><span class="variable-name">x</span><span class="tuareg-font-lock-operator">,</span><span class="variable-name"> _</span><span class="tuareg-font-lock-operator">)</span><span class="variable-name"> </span><span class="tuareg-font-lock-operator">-&gt;</span> x <span class="tuareg-font-lock-operator">&lt;=</span> min <span class="tuareg-font-lock-operator">||</span> x <span class="tuareg-font-lock-operator">&gt;=</span> max<span class="tuareg-font-lock-operator">)</span> bp<span class="tuareg-font-lock-operator">))</span> <span class="tuareg-font-lock-governing">in</span>
+ <span class="tuareg-font-lock-operator">...</span>
+ <span class="tuareg-font-lock-governing">let</span> <span class="variable-name">v </span><span class="tuareg-font-lock-operator">=</span>
+ <span class="type">F</span>.fix_b <span class="tuareg-font-lock-governing">begin</span> <span class="keyword">fun</span> <span class="variable-name">v </span><span class="tuareg-font-lock-operator">-&gt;</span>
+ <span class="type">F</span>.hold
+ init_v
+ <span class="tuareg-font-lock-operator">(</span><span class="type">F</span>.map
+ <span class="tuareg-font-lock-governing">begin</span> <span class="keyword">fun</span> <span class="variable-name">e </span><span class="tuareg-font-lock-operator">-&gt;</span>
+ <span class="tuareg-font-lock-governing">let</span><span class="variable-name"> </span><span class="tuareg-font-lock-operator">(</span><span class="variable-name">vx</span><span class="tuareg-font-lock-operator">,</span><span class="variable-name"> vy</span><span class="tuareg-font-lock-operator">)</span><span class="variable-name"> </span><span class="tuareg-font-lock-operator">=</span> <span class="type">F</span>.sample v <span class="tuareg-font-lock-governing">in</span>
+ <span class="tuareg-font-lock-governing">let</span><span class="variable-name"> </span><span class="tuareg-font-lock-operator">(</span><span class="variable-name">x</span><span class="tuareg-font-lock-operator">,</span><span class="variable-name"> y</span><span class="tuareg-font-lock-operator">)</span><span class="variable-name"> </span><span class="tuareg-font-lock-operator">=</span> <span class="type">F</span>.sample bp <span class="tuareg-font-lock-governing">in</span>
+ <span class="keyword">match</span> e <span class="keyword">with</span>
+ <span class="tuareg-font-lock-operator">|</span> `X_bounds <span class="tuareg-font-lock-operator">-&gt;</span> <span class="tuareg-font-lock-operator">(-.</span>vx<span class="tuareg-font-lock-operator">,</span> vy<span class="tuareg-font-lock-operator">)</span>
+ <span class="tuareg-font-lock-operator">|</span> `Y_bounds <span class="tuareg-font-lock-operator">-&gt;</span> <span class="tuareg-font-lock-operator">(</span>vx<span class="tuareg-font-lock-operator">,</span> <span class="tuareg-font-lock-operator">-.</span>vy<span class="tuareg-font-lock-operator">)</span>
+ <span class="tuareg-font-lock-operator">|</span> `Xy_bounds <span class="tuareg-font-lock-operator">-&gt;</span> <span class="tuareg-font-lock-operator">(-.</span>vx<span class="tuareg-font-lock-operator">,</span> <span class="tuareg-font-lock-operator">-.</span>vy<span class="tuareg-font-lock-operator">)</span>
+ <span class="tuareg-font-lock-operator">|</span> `Paddle <span class="tuareg-font-lock-operator">-&gt;</span>
+ <span class="tuareg-font-lock-governing">let</span><span class="variable-name"> </span><span class="tuareg-font-lock-operator">(</span><span class="variable-name">px</span><span class="tuareg-font-lock-operator">,</span><span class="variable-name"> py</span><span class="tuareg-font-lock-operator">)</span><span class="variable-name"> </span><span class="tuareg-font-lock-operator">=</span> <span class="type">F</span>.sample paddle_point <span class="tuareg-font-lock-governing">in</span>
+ <span class="comment">(* bounce v off the tangent to the paddle *)</span>
+ <span class="tuareg-font-lock-operator">...</span>
+ <span class="tuareg-font-lock-governing">end</span>
+ <span class="tuareg-font-lock-operator">(</span><span class="type">F</span>.merge <span class="tuareg-font-lock-operator">[</span> xy_out_of_bounds<span class="tuareg-font-lock-operator">;</span> x_out_of_bounds<span class="tuareg-font-lock-operator">;</span> y_out_of_bounds<span class="tuareg-font-lock-operator">;</span> hit_paddle <span class="tuareg-font-lock-operator">]))</span>
+ <span class="tuareg-font-lock-governing">end</span> <span class="tuareg-font-lock-governing">in</span>
+
+ <span class="type">F</span>.hold init_p
+ <span class="tuareg-font-lock-operator">(</span><span class="type">F</span>.collect
+ <span class="tuareg-font-lock-operator">(</span><span class="keyword">fun</span> <span class="tuareg-font-lock-operator">(</span><span class="variable-name">x</span><span class="tuareg-font-lock-operator">,</span><span class="variable-name"> y</span><span class="tuareg-font-lock-operator">)</span><span class="variable-name"> </span><span class="tuareg-font-lock-operator">()</span><span class="variable-name"> </span><span class="tuareg-font-lock-operator">-&gt;</span> <span class="tuareg-font-lock-governing">let</span> <span class="variable-name">vx</span><span class="tuareg-font-lock-operator">,</span><span class="variable-name"> vy </span><span class="tuareg-font-lock-operator">=</span> <span class="type">F</span>.sample v <span class="tuareg-font-lock-governing">in</span> <span class="tuareg-font-lock-operator">(</span>x <span class="tuareg-font-lock-operator">+.</span> vx<span class="tuareg-font-lock-operator">,</span> y <span class="tuareg-font-lock-operator">+.</span> vy<span class="tuareg-font-lock-operator">))</span>
+ init_p
+ <span class="tuareg-font-lock-operator">(</span><span class="type">Fd</span>.ticks 20.<span class="tuareg-font-lock-operator">))</span>
+</pre>
</div>
<div id="implementation" class="slide" style="display:none">
<h2>Implementation</h2>
<ul>
- <li>bunch of listeners :)</li>
+ <li>bunch of callbacks :)</li>
<li>nodes in dep graph assigned abstract timestamp at eval</li>
<li>notifications sent in timestamp order w/ priority queue</li>
<li>dependent functions delimit timestamp range;
@@ -178,7 +332,7 @@ <h2 id="title_email">jake@donham.org</h2>
<div id="why_ocaml" class="slide" style="display:none">
<h2>Why OCaml for FRP?</h2>
- <p>Claim: OCaml is a sweet-spot for FRP</p>
+ <p>OCaml is a sweet-spot for FRP:</p>
<ul>
<li>value-oriented mindset</li>
<li>higher-order functions</li>
@@ -187,7 +341,7 @@ <h2 id="title_email">jake@donham.org</h2>
<li>imperative implementation possible</li>
</ul>
- <p>Nonetheless there are FRP impls in many languages:</p>
+ <p>But FRP impls in many languages:</p>
<ul>
<li>Scheme (<a href="http://www.cs.brown.edu/~greg/">FrTime</a>), Javascript (<a href="http://www.flapjax-lang.org/">Flapjax</a>)</li>
<li>Haskell (<a href="http://conal.net/fran/">Fran</a> and many descendents)</li>
@@ -195,6 +349,42 @@ <h2 id="title_email">jake@donham.org</h2>
</ul>
</div>
+ <div id="reactive" class="slide" style="display:none">
+ <h2>FRP for reactive programming?</h2>
+
+ <p>Most programming is reactive programming (?)</p>
+ <ul>
+ <li>program is in some state</li>
+ <li>s**t happens (timer goes off, input arrives)</li>
+ <li>program reacts, changing state (and usually responding with output)</li>
+ </ul>
+
+ <p>e.g.</p>
+ <ul>
+ <li>industrial control systems</li>
+ <li>network stacks</li>
+ <li>event-driven servers</li>
+ </ul>
+
+ <p>is there a role for FRP outside of GUIs?</p>
+ </div>
+
+ <div id="thanks" class="slide" style="display:none">
+ <h2>Thanks!</h2>
+ <p>Takeaways:</p>
+ <ul>
+ <li>functional programming is important</li>
+ <li>immutable values are important</li>
+ <li>types are important</li>
+ </ul>
+
+ <p>Code:</p>
+ <ul>
+ <li><a href="http://github.com/jaked/ocamljs">http://github.com/jaked/ocamljs</a></li>
+ <li><a href="http://github.com/jaked/froc">http://github.com/jaked/froc</a></li>
+ </ul>
+ </div>
+
</div>
<div id="nav" style="display:none">
View
8 examples/froc-dom/talk/style.css
@@ -1,3 +1,7 @@
+h1 {
+ font-size: 64px;
+}
+
.slide {
font-size: 32px;
}
@@ -42,7 +46,7 @@
#title_email {
position: absolute;
left: 400px;
- top: 600px;
+ top: 650px;
}
li {
@@ -51,6 +55,8 @@ li {
/* htmlize stuff */
.comment {
+ /* font-lock-comment-delimiter-face */
+ color: #cd0000;
}
.comment-delimiter {
/* font-lock-comment-delimiter-face */
View
295 examples/froc-dom/talk/talk.ml
@@ -3,6 +3,8 @@ module F = Froc
module Fd = Froc_dom
module Fda = Froc_dom_anim
+open Ocamljs.Inline
+
let (|>) x f = f x
DEFINE DEBUG
@@ -47,17 +49,25 @@ module P =
struct
let pages = [
"title";
- "reactive";
- "gui";
+ "gui_hard";
+ "gui_not_hard";
"mvc";
"frp";
"ocaml_etc";
- "frp_events";
"frp_behaviors";
+ "frp_events";
"dyn_deps";
"output";
+ "clicks";
+ "clicks_src";
+ "sudoku";
+ "sudoku_src";
+ "bounce";
+ "bounce_src";
"implementation";
"why_ocaml";
+ "reactive";
+ "thanks";
]
let next = Util.next pages
@@ -65,34 +75,255 @@ struct
let number = Util.number pages
end
-module Title =
+module Clicks =
struct
- let title_sizes = [
- "Reactive Programming", 100;
- "<em>Functional</em> Reactive Programming", 68;
- "Functional Reactive Programming in <em>OCaml</em>", 52;
- "Functional Reactive Programming in OCaml and <em>Javascript</em>", 38;
- "Functional Reactive Programming in OCaml and Javascript with <em>ocamljs</em> and <em>froc</em>", 28;
- ]
- let titles = List.map fst title_sizes
-
- let next = Util.next titles
- let prev = Util.prev titles
-
- let page () =
- let title =
- Fd.keyEvent "keydown" Dom.document |>
- F.collect
- (fun p e ->
- match e#_get_keyCode with
- | 38 -> prev p
- | 40 -> next p
- | _ -> p)
- (List.hd titles) |>
- F.hold (List.hd titles) in
- let tt = Dom.document#getElementById "title_title" in
- Fd.attach_innerHTML_b tt title;
- Fd.attach_fontSize_b tt (F.blift title (fun t -> string_of_int (List.assoc t title_sizes) ^ "px"));
+ let onload () =
+ let elem id = D.document#getElementById id in
+ let clicks = F.count (Fd.clicks (elem "clicks_click")) in
+ let ticks = F.count (Fd.ticks 1000.) in
+ Fd.attach_innerHTML_b (elem "clicks_clicks") (F.lift string_of_int clicks);
+ Fd.attach_innerHTML_b (elem "clicks_seconds") (F.lift string_of_int ticks);
+ Fd.attach_innerHTML_b
+ (elem "clicks_difference")
+ (F.lift2
+ (fun clicks ticks ->
+ if clicks = ticks
+ then "same number of clicks as ticks"
+ else if clicks > ticks
+ then string_of_int (clicks - ticks) ^ " more clicks than ticks"
+ else string_of_int (ticks - clicks) ^ " more ticks than clicks")
+ clicks ticks)
+end
+
+module Sudoku =
+struct
+ let d = D.document
+
+ let (>>=) = F.(>>=)
+
+ let make_board () =
+ let make_input () =
+ let input = (d#createElement "input" : D.input) in
+ input#setAttribute "type" "text";
+ input#_set_size 1;
+ input#_set_maxLength 1;
+ let style = input#_get_style in
+ style#_set_border "none";
+ style#_set_padding "0px";
+ style#_set_fontSize "20px";
+
+ let (cell, set) = F.make_cell None in
+ Fd.attach_input_value_b input
+ (cell >>= function
+ | None -> F.return ""
+ | Some v -> F.return (string_of_int v));
+ let ev =
+ F.map
+ (function
+ | "1" | "2" | "3" | "4" | "5"
+ | "6" | "7" | "8" | "9" as v -> Some (int_of_string v)
+ | _ -> None)
+ (Fd.input_value_e input) in
+ F.notify_e ev set;
+ (cell, set, input) in
+
+ let rows =
+ Array.init 9 (fun i ->
+ Array.init 9 (fun j ->
+ make_input ())) in
+
+ let foldi x a f =
+ let r = ref x in
+ for i = 0 to Array.length a - 1 do
+ r := f i !r (Array.unsafe_get a i)
+ done;
+ !r in
+
+ let adjacents i j =
+ let adj i' j' =
+ (i' <> i || j' <> j) &&
+ (i' = i or j' = j or
+ (i' / 3 = i / 3 && j' / 3 = j / 3)) in
+ foldi [] rows begin fun i' adjs row ->
+ foldi adjs row begin fun j' adjs (cell,_,_) ->
+ if adj i' j'
+ then cell::adjs
+ else adjs
+ end
+ end in
+
+ ArrayLabels.iteri rows ~f:begin fun i row ->
+ ArrayLabels.iteri row ~f:begin fun j (cell, _, input) ->
+ let adjs = adjacents i j in
+ Fd.attach_backgroundColor_b input
+ (F.bindN adjs (fun adjs ->
+ F.lift
+ (fun v ->
+ if v <> None && List.mem v adjs
+ then "#ff0000"
+ else "#ffffff")
+ cell))
+ end
+ end;
+
+ let make_td i j input =
+ let td = d#createElement "td" in
+ let style = td#_get_style in
+ style#_set_borderStyle "solid";
+ style#_set_borderColor "#000000";
+ let widths = function
+ | 0 -> 2, 0 | 2 -> 1, 1 | 3 -> 1, 0
+ | 5 -> 1, 1 | 6 -> 1, 0 | 8 -> 1, 2
+ | _ -> 1, 0 in
+ let (top, bottom) = widths i in
+ let (left, right) = widths j in
+ let px k = string_of_int k ^ "px" in
+ style#_set_borderTopWidth (px top);
+ style#_set_borderBottomWidth (px bottom);
+ style#_set_borderLeftWidth (px left);
+ style#_set_borderRightWidth (px right);
+ ignore (td#appendChild input);
+ td in
+
+ let table = d#createElement "table" in
+ table#setAttribute "cellpadding" "0px";
+ table#setAttribute "cellspacing" "0px";
+ let tbody = d#createElement "tbody" in
+ ignore (table#appendChild tbody);
+ ArrayLabels.iteri rows ~f:(fun i row ->
+ let tr = d#createElement "tr" in
+ ArrayLabels.iteri row ~f:(fun j (_,_,input) ->
+ let td = make_td i j input in
+ ignore (tr#appendChild td));
+ ignore (tbody#appendChild tr));
+
+ (rows, table)
+
+ let get_board rows =
+ ArrayLabels.iter rows ~f:(fun row ->
+ ArrayLabels.iter row ~f:(fun (_,set,_) ->
+ set None));
+ let board = << [[[5], 0, 0, [6], [4], 0, [9], 0, [8]], [0, 0, [2], 0, 0, 0, 0, 0, 0], [[9], 0, 0, 0, 0, [2], 0, 0, 0], [[8], 0, 0, [3], [2], 0, 0, [1], 0], [0, [4], 0, [1], 0, [5], 0, [8], 0], [0, [6], 0, 0, [7], [8], 0, 0, [5]], [0, 0, 0, [2], 0, 0, 0, 0, [9]], [0, 0, 0, 0, 0, 0, [7], 0, 0], [[3], 0, [4], 0, [8], [9], 0, 0, [6]]] >> in
+ for i = 0 to 8 do
+ for j = 0 to 8 do
+ let (_,set,input) = rows.(i).(j) in
+ let v = board.(i).(j) in
+ input#_set_disabled (v <> None);
+ set v;
+ done
+ done
+
+ let onload () =
+ let (rows, table) = make_board () in
+ get_board rows;
+ let board = d#getElementById "sudoku_board" in
+ F.cleanup (fun () -> ignore (board#removeChild table));
+ ignore (board#appendChild table)
+end
+
+module Bounce =
+struct
+ let get id = D.document#getElementById id
+
+ let onload () =
+ let paddle_radius = 25. in
+ let ball_radius = 5. in
+ let min = 0. in
+ let max = 500. in
+
+ let init_p = (Random.float max, Random.float max) in
+ let init_v = (Random.float 5., Random.float 5.) in
+
+ let offset_x, offset_y =
+ let c = get "bounce_canvas" in
+ c#_get_offsetLeft, c#_get_offsetTop in
+
+ let paddle_point =
+ F.blift (Fd.mouse_b ()) begin fun (x, y) ->
+ let x = x - offset_x in
+ let y = y - offset_y in
+ let x = float_of_int x in
+ let y = float_of_int y in
+ let x = if x < min then min else if x > max then max else x in
+ let y = if y < min then min else if y > max then max else y in
+ (x, y)
+ end in
+
+ let paddle = F.blift paddle_point (fun p -> Fda.disk p paddle_radius (Fda.color 255 0 0)) in
+
+ let ball_point =
+ F.fix_b begin fun bp ->
+
+ let x_out_of_bounds =
+ F.map
+ (fun () -> `X_bounds)
+ (F.when_true
+ (F.blift bp (fun (x, _) -> x <= min || x >= max))) in
+
+ let y_out_of_bounds =
+ F.map
+ (fun () -> `Y_bounds)
+ (F.when_true
+ (F.blift bp (fun (_, y) -> y <= min || y >= max))) in
+
+ (*
+ the merge below reports only the leftmost simultaneous event,
+ so we have to account for the combination to avoid losing the
+ ball in the corners. is there a more compositional way?
+ *)
+ let xy_out_of_bounds =
+ F.map2 (fun _ _ -> `Xy_bounds) x_out_of_bounds y_out_of_bounds in
+
+ let hit_paddle =
+ F.map (fun () -> `Paddle)
+ (F.when_true
+ (F.blift bp begin fun (x, y) ->
+ let (px, py) = F.sample paddle_point in
+ let dist_x = px -. x in
+ let dist_y = py -. y in
+ let dist = paddle_radius +. ball_radius in
+ dist_x *. dist_x +. dist_y *. dist_y <= dist *. dist
+ end)) in
+
+ let v =
+ F.fix_b begin fun v ->
+ F.hold
+ init_v
+ (F.map
+ begin fun e ->
+ let (vx, vy) = F.sample v in
+ let (x, y) = F.sample bp in
+ match e with
+ | `X_bounds -> (-.vx, vy)
+ | `Y_bounds -> (vx, -.vy)
+ | `Xy_bounds -> (-.vx, -.vy)
+ | `Paddle ->
+ (* bounce v off the tangent to the paddle *)
+ let (px, py) = F.sample paddle_point in
+ let (nx, ny) =
+ let (nx, ny) = (x -. px, y -. py) in
+ let z = sqrt (nx *. nx +. ny *. ny) in
+ (nx /. z, ny /. z) in
+ let dp = vx *. nx +. vy *. ny in
+ (vx -. 2. *. dp *. nx, vy -. 2. *. dp *. ny)
+ end
+ (F.merge [ xy_out_of_bounds; x_out_of_bounds; y_out_of_bounds; hit_paddle ]))
+ end in
+
+ F.hold init_p
+ (F.collect
+ (fun (x, y) () -> let vx, vy = F.sample v in (x +. vx, y +. vy))
+ init_p
+ (Fd.ticks 20.))
+ end in
+
+ let ball =
+ F.blift ball_point begin fun (x, y) ->
+ Fda.disk (x, y) ball_radius (Fda.color 0 255 0)
+ end in
+
+ let shapes = F.bliftN [ paddle; ball ] (fun shapes -> shapes) in
+ Froc_dom_anim.attach (get "bounce_canvas") shapes
end
let onload () =
@@ -133,7 +364,9 @@ let onload () =
(* page-specific stuff *)
F.blift page
begin function
- | "title" -> Title.page ()
+ | "clicks"-> Clicks.onload ()
+ | "sudoku" -> Sudoku.onload ()
+ | "bounce" -> Bounce.onload ()
| _ -> ()
end |> ignore
Please sign in to comment.
Something went wrong with that request. Please try again.