summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormuqiuhan <[email protected]>2024-09-12 13:04:16 +0000
committermuqiuhan <[email protected]>2024-09-12 13:04:16 +0000
commite7205eb89f49a850c7efaf2b72bb9009c234c559 (patch)
treecdf338848fbdd5ca82d2350a67aa122578a698b8
parenta7dc420909364a0994c30cfe1487b87a1967f650 (diff)
downloadblog-e7205eb89f49a850c7efaf2b72bb9009c234c559.tar.gz
deploy: 13e94aed5e18c84970436e3c53db64029253eadd
-rw-r--r--2024/09/01/二〇二四年九月一日/index.html (renamed from 2024/09/12/二〇二四年九月一日/index.html)9
-rw-r--r--2024/09/01/藿香正气水和藿香正气口服液的区别/index.html2
-rw-r--r--2024/09/07/给孩子们的话/index.html2
-rw-r--r--2024/09/12/Functional-Reactive-Programming-in-F/index.html379
-rw-r--r--2024/09/12/二〇二四年九月十二日/index.html2
-rw-r--r--archives/2024/09/index.html22
-rw-r--r--archives/2024/index.html40
-rw-r--r--archives/2024/page/2/index.html38
-rw-r--r--archives/2024/page/3/index.html38
-rw-r--r--archives/2024/page/4/index.html224
-rw-r--r--archives/index.html38
-rw-r--r--archives/page/10/index.html18
-rw-r--r--archives/page/2/index.html36
-rw-r--r--archives/page/3/index.html36
-rw-r--r--archives/page/4/index.html38
-rw-r--r--archives/page/5/index.html36
-rw-r--r--archives/page/6/index.html36
-rw-r--r--archives/page/7/index.html36
-rw-r--r--archives/page/8/index.html36
-rw-r--r--archives/page/9/index.html36
-rw-r--r--categories/gallery/index.html14
-rw-r--r--search.xml125
-rw-r--r--tags/F/index.html18
-rw-r--r--tags/Technique/index.html36
-rw-r--r--tags/Technique/page/2/index.html38
-rw-r--r--tags/Technique/page/3/index.html18
26 files changed, 1079 insertions, 272 deletions
diff --git a/2024/09/12/二〇二四年九月一日/index.html b/2024/09/01/二〇二四年九月一日/index.html
index 565bd171..5995bf42 100644
--- a/2024/09/12/二〇二四年九月一日/index.html
+++ b/2024/09/01/二〇二四年九月一日/index.html
@@ -148,7 +148,7 @@
</div>
<span class="post-date">
- Sep 12, 2024
+ Sep 1, 2024
</span>
</div>
<div class="post-img">
@@ -178,12 +178,17 @@
<nav class="post-nav">
<div class="prev-item">
+ <div class="icon arrow-left"></div>
+ <div class="post-link">
+ <a href="/2024/09/07/%E7%BB%99%E5%AD%A9%E5%AD%90%E4%BB%AC%E7%9A%84%E8%AF%9D/">Prev</a>
+ </div>
+
</div>
<div class="next-item">
<div class="icon arrow-right"></div>
<div class="post-link">
- <a href="/2024/09/12/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E5%8D%81%E4%BA%8C%E6%97%A5/">Next</a>
+ <a href="/2024/09/01/%E8%97%BF%E9%A6%99%E6%AD%A3%E6%B0%94%E6%B0%B4%E5%92%8C%E8%97%BF%E9%A6%99%E6%AD%A3%E6%B0%94%E5%8F%A3%E6%9C%8D%E6%B6%B2%E7%9A%84%E5%8C%BA%E5%88%AB/">Next</a>
</div>
</div>
diff --git a/2024/09/01/藿香正气水和藿香正气口服液的区别/index.html b/2024/09/01/藿香正气水和藿香正气口服液的区别/index.html
index 2d295757..5f7d0dff 100644
--- a/2024/09/01/藿香正气水和藿香正气口服液的区别/index.html
+++ b/2024/09/01/藿香正气水和藿香正气口服液的区别/index.html
@@ -187,7 +187,7 @@
<div class="icon arrow-left"></div>
<div class="post-link">
- <a href="/2024/09/07/%E7%BB%99%E5%AD%A9%E5%AD%90%E4%BB%AC%E7%9A%84%E8%AF%9D/">Prev</a>
+ <a href="/2024/09/01/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E4%B8%80%E6%97%A5/">Prev</a>
</div>
</div>
diff --git a/2024/09/07/给孩子们的话/index.html b/2024/09/07/给孩子们的话/index.html
index 3bd0da46..8f5dfbd3 100644
--- a/2024/09/07/给孩子们的话/index.html
+++ b/2024/09/07/给孩子们的话/index.html
@@ -191,7 +191,7 @@
<div class="icon arrow-right"></div>
<div class="post-link">
- <a href="/2024/09/01/%E8%97%BF%E9%A6%99%E6%AD%A3%E6%B0%94%E6%B0%B4%E5%92%8C%E8%97%BF%E9%A6%99%E6%AD%A3%E6%B0%94%E5%8F%A3%E6%9C%8D%E6%B6%B2%E7%9A%84%E5%8C%BA%E5%88%AB/">Next</a>
+ <a href="/2024/09/01/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E4%B8%80%E6%97%A5/">Next</a>
</div>
</div>
diff --git a/2024/09/12/Functional-Reactive-Programming-in-F/index.html b/2024/09/12/Functional-Reactive-Programming-in-F/index.html
new file mode 100644
index 00000000..dd82f7a2
--- /dev/null
+++ b/2024/09/12/Functional-Reactive-Programming-in-F/index.html
@@ -0,0 +1,379 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+<meta name="viewport"
+ content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+<meta http-equiv="X-UA-Compatible" content="ie=edge">
+
+ <meta name="author" content="韩暮秋">
+
+
+ <meta name="subtitle" content="暮秋小屋">
+
+
+ <meta name="description" content="这里是暮秋小屋,思念和灵感的寄存处">
+
+
+ <meta name="keywords" content="韩暮秋,MuqiuHan,'Muqiu Han', 'muqiu han', muqiuhan">
+
+
+
+
+<title>Functional Reactive Programming in F# | 暮秋小屋</title>
+
+
+
+ <link rel="icon" href="/favicon.ico">
+
+
+
+<style>
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;700&family=Roboto+Mono&display=swap');
+</style>
+
+
+
+ <!-- stylesheets list from _config.yml -->
+
+ <link rel="stylesheet" href="/css/style.css">
+
+
+
+
+
+ <!-- scripts list from _config.yml -->
+
+ <script src="/js/frame.js"></script>
+
+
+
+
+
+ <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
+ <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
+
+
+
+
+
+
+
+ <meta name="generator" content="Hexo 6.3.0"></head>
+ <body>
+ <div class="mask-border">
+ </div>
+
+ <div class="wrapper">
+
+ <div class="header">
+ <div class="flex-container">
+ <div class="header-inner">
+ <div class="site-brand-container">
+ <a href="/">
+
+ 暮秋小屋
+
+ </a>
+ </div>
+ <div id="menu-btn" class="menu-btn" onclick="toggleMenu()">
+ Menu
+ </div>
+ <nav class="site-nav">
+ <ul class="menu-list">
+
+
+ <li class="menu-item">
+ <a href="/">主页</a>
+ </li>
+
+
+
+ <li class="menu-item">
+ <a href="/categories/gallery/">日记本</a>
+ </li>
+
+
+
+ <li class="menu-item">
+ <a href="/tags/Medicine/">医学</a>
+ </li>
+
+
+
+ <li class="menu-item">
+ <a href="/tags/Technique/">计算机科学</a>
+ </li>
+
+
+
+ <li class="menu-item">
+ <a href="/tags/Life/">生活</a>
+ </li>
+
+
+
+ <li class="menu-item">
+ <a href="/about">关于</a>
+ </li>
+
+
+
+ <li class="menu-item search-btn">
+ <a href="#">Search</a>
+ </li>
+
+ </ul>
+ </nav>
+ </div>
+ </div>
+</div>
+
+
+ <div class="main">
+ <div class="flex-container">
+ <article id="post">
+
+
+ <div class="post-head">
+ <div class="post-info">
+ <div class="tag-list">
+
+
+ <span class="post-tag">
+ <a href="/tags/Technique/">
+ Technique
+ </a>
+ </span>
+
+ <span class="post-tag">
+ <a href="/tags/F/">
+ F#
+ </a>
+ </span>
+
+
+ </div>
+ <div class="post-title">
+
+
+ Functional Reactive Programming in F#
+
+
+ </div>
+ <span class="post-date">
+ Sep 12, 2024
+ </span>
+ </div>
+ <div class="post-img">
+
+ <div class="h-line-primary"></div>
+
+ </div>
+</div>
+ <div class="post-content">
+ <p>Events are everywhere. Almost every program has to handle events, whether it be button clicks in the user interface, listening to sockets in a server, or even a system shutdown notification.</p>
+<p>And events are the basis of one of the most common OO design patterns: the “Observer” pattern.</p>
+<p>But as we know, event handling, like concurrency in general, can be tricky to implement. Simple event logic is straightforward, but what about logic like “do something if two events happen in a row but do something different if only one event happens” or “do something if two events happen at roughly the same time”. And how easy is it to combine these requirements in other, more complex ways?</p>
+<p>Even if you can successfully implement these requirements, the code tends to be spaghetti like and hard to understand, even with the best intentions.</p>
+<p>Is there an approach that can make event handling easier?</p>
+<p>We saw in the previous post on message queues that one of the advantages of that approach was that the requests were “serialized” making it conceptually easier to deal with.</p>
+<p>There is a similar approach that can be used with events. The idea is to turn a series of events into an “event stream”. Event streams then become quite like IEnumerables, and so the obvious next step is to treat them in much the the same way that LINQ handles collections, so that they can be filtered, mapped, split and combined.</p>
+<p>F# has built in support for this model, as well as for the more traditional approach.</p>
+<h2 id="A-simple-event-stream"><a href="#A-simple-event-stream" class="headerlink" title="A simple event stream"></a>A simple event stream</h2><p>Let’s start with a simple example to compare the two approaches. We’ll implement the classic event handler approach first.</p>
+<p>First, we define a utility function that will:</p>
+<ul>
+<li>create a timer</li>
+<li>register a handler for the <code>Elapsed</code> event</li>
+<li>run the timer for five seconds and then stop it</li>
+</ul>
+<p>Here’s the code:</p>
+<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">open</span> System</span><br><span class="line"><span class="keyword">open</span> System.Threading</span><br><span class="line"></span><br><span class="line"><span class="comment">/// create a timer and register an event handler,</span></span><br><span class="line"><span class="comment">/// then run the timer for five seconds</span></span><br><span class="line"><span class="keyword">let</span> createTimer timerInterval eventHandler <span class="operator">=</span></span><br><span class="line"> <span class="comment">// setup a timer</span></span><br><span class="line"> <span class="keyword">let</span> timer <span class="operator">=</span> <span class="keyword">new</span> System.Timers.Timer(float timerInterval)</span><br><span class="line"> timer.AutoReset <span class="operator">&lt;-</span> <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// add an event handler</span></span><br><span class="line"> timer.Elapsed.Add eventHandler</span><br><span class="line"></span><br><span class="line"> <span class="comment">// return an async task</span></span><br><span class="line"> <span class="keyword">async</span> &#123;</span><br><span class="line"> <span class="comment">// start timer...</span></span><br><span class="line"> timer.Start()</span><br><span class="line"> <span class="comment">// ...run for five seconds...</span></span><br><span class="line"> <span class="keyword">do!</span> Async.Sleep <span class="number">5000</span></span><br><span class="line"> <span class="comment">// ... and stop</span></span><br><span class="line"> timer.Stop()</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
+
+<p>Now test it interactively:</p>
+<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// create a handler. The event args are ignored</span></span><br><span class="line"><span class="keyword">let</span> basicHandler _ <span class="operator">=</span> <span class="built_in">printfn</span> <span class="string">&quot;tick %A&quot;</span> DateTime.Now</span><br><span class="line"></span><br><span class="line"><span class="comment">// register the handler</span></span><br><span class="line"><span class="keyword">let</span> basicTimer1 <span class="operator">=</span> createTimer <span class="number">1000</span> basicHandler</span><br><span class="line"></span><br><span class="line"><span class="comment">// run the task now</span></span><br><span class="line">Async.RunSynchronously basicTimer1</span><br></pre></td></tr></table></figure>
+
+<p>Now let’s create a similar utility method to create a timer, but this time it will return an “observable” as well, which is the stream of events.</p>
+<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> createTimerAndObservable timerInterval <span class="operator">=</span></span><br><span class="line"> <span class="comment">// setup a timer</span></span><br><span class="line"> <span class="keyword">let</span> timer <span class="operator">=</span> <span class="keyword">new</span> System.Timers.Timer(float timerInterval)</span><br><span class="line"> timer.AutoReset <span class="operator">&lt;-</span> <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// events are automatically IObservable</span></span><br><span class="line"> <span class="keyword">let</span> observable <span class="operator">=</span> timer.Elapsed</span><br><span class="line"></span><br><span class="line"> <span class="comment">// return an async task</span></span><br><span class="line"> <span class="keyword">let</span> task <span class="operator">=</span> <span class="keyword">async</span> &#123;</span><br><span class="line"> timer.Start()</span><br><span class="line"> <span class="keyword">do!</span> Async.Sleep <span class="number">5000</span></span><br><span class="line"> timer.Stop()</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// return a async task and the observable</span></span><br><span class="line"> (task,observable)</span><br></pre></td></tr></table></figure>
+
+<p>And again test it interactively:</p>
+<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// create the timer and the corresponding observable</span></span><br><span class="line"><span class="keyword">let</span> basicTimer2 , timerEventStream <span class="operator">=</span> createTimerAndObservable <span class="number">1000</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// register that every time something happens on the</span></span><br><span class="line"><span class="comment">// event stream, print the time.</span></span><br><span class="line">timerEventStream</span><br><span class="line"><span class="operator">|&gt;</span> Observable.subscribe (<span class="keyword">fun</span> _ <span class="operator">-&gt;</span> <span class="built_in">printfn</span> <span class="string">&quot;tick %A&quot;</span> DateTime.Now)</span><br><span class="line"></span><br><span class="line"><span class="comment">// run the task now</span></span><br><span class="line">Async.RunSynchronously basicTimer2</span><br></pre></td></tr></table></figure>
+
+<p>The difference is that instead of registering a handler directly with an event, we are “subscribing” to an event stream. Subtly different, and important.</p>
+<h2 id="Counting-events"><a href="#Counting-events" class="headerlink" title="Counting events"></a>Counting events</h2><p>In this next example, we’ll have a slightly more complex requirement:</p>
+<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Create a timer that ticks every 500ms.</span><br><span class="line">At each tick, print the number of ticks so far and the current time.</span><br></pre></td></tr></table></figure>
+
+<p>To do this in a classic imperative way, we would probably create a class with a mutable counter, as below:</p>
+<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> <span class="title class_">ImperativeTimerCount</span>() <span class="operator">=</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mutable</span> count <span class="operator">=</span> <span class="number">0</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// the event handler. The event args are ignored</span></span><br><span class="line"> <span class="keyword">member</span> this.handleEvent _ <span class="operator">=</span></span><br><span class="line"> count <span class="operator">&lt;-</span> count <span class="operator">+</span> <span class="number">1</span></span><br><span class="line"> <span class="built_in">printfn</span> <span class="string">&quot;timer ticked with count %i&quot;</span> count</span><br></pre></td></tr></table></figure>
+
+<p>We can reuse the utility functions we created earlier to test it:</p>
+<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// create a handler class</span></span><br><span class="line"><span class="keyword">let</span> handler <span class="operator">=</span> <span class="keyword">new</span> ImperativeTimerCount()</span><br><span class="line"></span><br><span class="line"><span class="comment">// register the handler method</span></span><br><span class="line"><span class="keyword">let</span> timerCount1 <span class="operator">=</span> createTimer <span class="number">500</span> handler.handleEvent</span><br><span class="line"></span><br><span class="line"><span class="comment">// run the task now</span></span><br><span class="line">Async.RunSynchronously timerCount1</span><br></pre></td></tr></table></figure>
+
+<p>Let’s see how we would do this same thing in a functional way:</p>
+<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// create the timer and the corresponding observable</span></span><br><span class="line"><span class="keyword">let</span> timerCount2, timerEventStream <span class="operator">=</span> createTimerAndObservable <span class="number">500</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// set up the transformations on the event stream</span></span><br><span class="line">timerEventStream</span><br><span class="line"><span class="operator">|&gt;</span> Observable.scan (<span class="keyword">fun</span> count _ <span class="operator">-&gt;</span> count <span class="operator">+</span> <span class="number">1</span>) <span class="number">0</span></span><br><span class="line"><span class="operator">|&gt;</span> Observable.subscribe (<span class="keyword">fun</span> count <span class="operator">-&gt;</span> <span class="built_in">printfn</span> <span class="string">&quot;timer ticked with count %i&quot;</span> count)</span><br><span class="line"></span><br><span class="line"><span class="comment">// run the task now</span></span><br><span class="line">Async.RunSynchronously timerCount2</span><br></pre></td></tr></table></figure>
+
+<p>Here we see how you can build up layers of event transformations, just as you do with list transformations in LINQ.</p>
+<p>The first transformation is <code>scan</code>, which accumulates state for each event. It is roughly equivalent to the <code>List.fold</code> function that we have seen used with lists. In this case, the accumulated state is just a counter.</p>
+<p>And then, for each event, the count is printed out.</p>
+<p>Note that in this functional approach, we didn’t have any mutable state, and we didn’t need to create any special classes.</p>
+<h2 id="Merging-multiple-event-streams"><a href="#Merging-multiple-event-streams" class="headerlink" title="Merging multiple event streams"></a>Merging multiple event streams</h2><p>For a final example, we’ll look at merging multiple event streams.</p>
+<p>Let’s make a requirement based on the well-known “FizzBuzz” problem:</p>
+<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">Create two timers, called &#x27;3&#x27; and &#x27;5&#x27;. The &#x27;3&#x27; timer ticks every 300ms and the &#x27;5&#x27; timer ticks</span><br><span class="line">every 500ms.</span><br><span class="line"></span><br><span class="line">Handle the events as follows:</span><br><span class="line">a) for all events, print the id of the time and the time</span><br><span class="line">b) when a tick is simultaneous with a previous tick, print &#x27;FizzBuzz&#x27;</span><br><span class="line">otherwise:</span><br><span class="line">c) when the &#x27;3&#x27; timer ticks on its own, print &#x27;Fizz&#x27;</span><br><span class="line">d) when the &#x27;5&#x27; timer ticks on its own, print &#x27;Buzz&#x27;</span><br></pre></td></tr></table></figure>
+
+<p>First let’s create some code that both implementations can use.</p>
+<p>We’ll want a generic event type that captures the timer id and the time of the tick.</p>
+<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> <span class="title class_">FizzBuzzEvent</span> <span class="operator">=</span> &#123;label<span class="operator">:</span><span class="type">int</span>; time<span class="operator">:</span> DateTime&#125;</span><br></pre></td></tr></table></figure>
+
+<p>And then we need a utility function to see if two events are simultaneous. We’ll be generous and allow a time difference of up to 50ms.</p>
+<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> areSimultaneous (earlierEvent,laterEvent) <span class="operator">=</span></span><br><span class="line"> <span class="keyword">let</span> &#123;label<span class="operator">=</span>_;time<span class="operator">=</span>t1&#125; <span class="operator">=</span> earlierEvent</span><br><span class="line"> <span class="keyword">let</span> &#123;label<span class="operator">=</span>_;time<span class="operator">=</span>t2&#125; <span class="operator">=</span> laterEvent</span><br><span class="line"> t2.Subtract(t1).Milliseconds <span class="operator">&lt;</span> <span class="number">50</span></span><br></pre></td></tr></table></figure>
+
+<p>In the imperative design, we’ll need to keep track of the previous event, so we can compare them. And we’ll need special case code for the first time, when the previous event doesn’t exist</p>
+<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> <span class="title class_">ImperativeFizzBuzzHandler</span>() <span class="operator">=</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mutable</span> previousEvent<span class="operator">:</span> FizzBuzzEvent <span class="type">option</span> <span class="operator">=</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> printEvent thisEvent <span class="operator">=</span></span><br><span class="line"> <span class="keyword">let</span> &#123;label<span class="operator">=</span><span class="built_in">id</span>; time<span class="operator">=</span>t&#125; <span class="operator">=</span> thisEvent</span><br><span class="line"> <span class="built_in">printf</span> <span class="string">&quot;[%i] %i.%03i &quot;</span> <span class="built_in">id</span> t.Second t.Millisecond</span><br><span class="line"> <span class="keyword">let</span> simultaneous <span class="operator">=</span> previousEvent.IsSome <span class="operator">&amp;&amp;</span> areSimultaneous (previousEvent.Value,thisEvent)</span><br><span class="line"> <span class="keyword">if</span> simultaneous <span class="keyword">then</span> <span class="built_in">printfn</span> <span class="string">&quot;FizzBuzz&quot;</span></span><br><span class="line"> <span class="keyword">elif</span> <span class="built_in">id</span> <span class="operator">=</span> <span class="number">3</span> <span class="keyword">then</span> <span class="built_in">printfn</span> <span class="string">&quot;Fizz&quot;</span></span><br><span class="line"> <span class="keyword">elif</span> <span class="built_in">id</span> <span class="operator">=</span> <span class="number">5</span> <span class="keyword">then</span> <span class="built_in">printfn</span> <span class="string">&quot;Buzz&quot;</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">member</span> this.handleEvent3 eventArgs <span class="operator">=</span></span><br><span class="line"> <span class="keyword">let</span> event <span class="operator">=</span> &#123;label<span class="operator">=</span><span class="number">3</span>; time<span class="operator">=</span>DateTime.Now&#125;</span><br><span class="line"> printEvent event</span><br><span class="line"> previousEvent <span class="operator">&lt;-</span> <span class="literal">Some</span> event</span><br><span class="line"></span><br><span class="line"> <span class="keyword">member</span> this.handleEvent5 eventArgs <span class="operator">=</span></span><br><span class="line"> <span class="keyword">let</span> event <span class="operator">=</span> &#123;label<span class="operator">=</span><span class="number">5</span>; time<span class="operator">=</span>DateTime.Now&#125;</span><br><span class="line"> printEvent event</span><br><span class="line"> previousEvent <span class="operator">&lt;-</span> <span class="literal">Some</span> event</span><br></pre></td></tr></table></figure>
+
+<p>Now the code is beginning to get ugly fast! Already we have mutable state, complex conditional logic, and special cases, just for such a simple requirement.</p>
+<p>Let’s test it:</p>
+<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// create the class</span></span><br><span class="line"><span class="keyword">let</span> handler <span class="operator">=</span> <span class="keyword">new</span> ImperativeFizzBuzzHandler()</span><br><span class="line"></span><br><span class="line"><span class="comment">// create the two timers and register the two handlers</span></span><br><span class="line"><span class="keyword">let</span> timer3 <span class="operator">=</span> createTimer <span class="number">300</span> handler.handleEvent3</span><br><span class="line"><span class="keyword">let</span> timer5 <span class="operator">=</span> createTimer <span class="number">500</span> handler.handleEvent5</span><br><span class="line"></span><br><span class="line"><span class="comment">// run the two timers at the same time</span></span><br><span class="line">[timer3;timer5]</span><br><span class="line"><span class="operator">|&gt;</span> Async.Parallel</span><br><span class="line"><span class="operator">|&gt;</span> Async.RunSynchronously</span><br></pre></td></tr></table></figure>
+
+<p>It does work, but are you sure the code is not buggy? Are you likely to accidentally break something if you change it?</p>
+<p>The problem with this imperative code is that it has a lot of noise that obscures the the requirements.</p>
+<p>Can the functional version do better? Let’s see!</p>
+<p>First, we create <em>two</em> event streams, one for each timer:</p>
+<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> timer3, timerEventStream3 <span class="operator">=</span> createTimerAndObservable <span class="number">300</span></span><br><span class="line"><span class="keyword">let</span> timer5, timerEventStream5 <span class="operator">=</span> createTimerAndObservable <span class="number">500</span></span><br></pre></td></tr></table></figure>
+
+<p>Next, we convert each event on the “raw” event streams into our FizzBuzz event type:</p>
+<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// convert the time events into FizzBuzz events with the appropriate id</span></span><br><span class="line"><span class="keyword">let</span> eventStream3 <span class="operator">=</span></span><br><span class="line"> timerEventStream3</span><br><span class="line"> <span class="operator">|&gt;</span> Observable.map (<span class="keyword">fun</span> _ <span class="operator">-&gt;</span> &#123;label<span class="operator">=</span><span class="number">3</span>; time<span class="operator">=</span>DateTime.Now&#125;)</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> eventStream5 <span class="operator">=</span></span><br><span class="line"> timerEventStream5</span><br><span class="line"> <span class="operator">|&gt;</span> Observable.map (<span class="keyword">fun</span> _ <span class="operator">-&gt;</span> &#123;label<span class="operator">=</span><span class="number">5</span>; time<span class="operator">=</span>DateTime.Now&#125;)</span><br></pre></td></tr></table></figure>
+
+<p>Now, to see if two events are simultaneous, we need to compare them from the two different streams somehow.</p>
+<p>It’s actually easier than it sounds, because we can:</p>
+<ul>
+<li>combine the two streams into a single stream:</li>
+<li>then create pairs of sequential events</li>
+<li>then test the pairs to see if they are simultaneous</li>
+<li>then split the input stream into two new output streams based on that test</li>
+</ul>
+<p>Here’s the actual code to do this:</p>
+<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// combine the two streams</span></span><br><span class="line"><span class="keyword">let</span> combinedStream <span class="operator">=</span></span><br><span class="line"> Observable.merge eventStream3 eventStream5</span><br><span class="line"></span><br><span class="line"><span class="comment">// make pairs of events</span></span><br><span class="line"><span class="keyword">let</span> pairwiseStream <span class="operator">=</span></span><br><span class="line"> combinedStream <span class="operator">|&gt;</span> Observable.pairwise</span><br><span class="line"></span><br><span class="line"><span class="comment">// split the stream based on whether the pairs are simultaneous</span></span><br><span class="line"><span class="keyword">let</span> simultaneousStream, nonSimultaneousStream <span class="operator">=</span></span><br><span class="line"> pairwiseStream <span class="operator">|&gt;</span> Observable.partition areSimultaneous</span><br></pre></td></tr></table></figure>
+
+<p>Finally, we can split the <code>nonSimultaneousStream</code> again, based on the event id:</p>
+<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// split the non-simultaneous stream based on the id</span></span><br><span class="line"><span class="keyword">let</span> fizzStream, buzzStream <span class="operator">=</span></span><br><span class="line"> nonSimultaneousStream</span><br><span class="line"> <span class="comment">// convert pair of events to the first event</span></span><br><span class="line"> <span class="operator">|&gt;</span> Observable.map (<span class="keyword">fun</span> (ev1,_) <span class="operator">-&gt;</span> ev1)</span><br><span class="line"> <span class="comment">// split on whether the event id is three</span></span><br><span class="line"> <span class="operator">|&gt;</span> Observable.partition (<span class="keyword">fun</span> &#123;label<span class="operator">=</span><span class="built_in">id</span>&#125; <span class="operator">-&gt;</span> <span class="built_in">id</span><span class="operator">=</span><span class="number">3</span>)</span><br></pre></td></tr></table></figure>
+
+<p>Let’s review so far. We have started with the two original event streams and from them created four new ones:</p>
+<ul>
+<li><code>combinedStream</code> contains all the events</li>
+<li><code>simultaneousStream</code> contains only the simultaneous events</li>
+<li><code>fizzStream</code> contains only the non-simultaneous events with id&#x3D;3</li>
+<li><code>buzzStream</code> contains only the non-simultaneous events with id&#x3D;5</li>
+</ul>
+<p>Now all we need to do is attach behavior to each stream:</p>
+<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//print events from the combinedStream</span></span><br><span class="line">combinedStream</span><br><span class="line"><span class="operator">|&gt;</span> Observable.subscribe (<span class="keyword">fun</span> &#123;label<span class="operator">=</span><span class="built_in">id</span>;time<span class="operator">=</span>t&#125; <span class="operator">-&gt;</span></span><br><span class="line"> <span class="built_in">printf</span> <span class="string">&quot;[%i] %i.%03i &quot;</span> <span class="built_in">id</span> t.Second t.Millisecond)</span><br><span class="line"></span><br><span class="line"><span class="comment">//print events from the simultaneous stream</span></span><br><span class="line">simultaneousStream</span><br><span class="line"><span class="operator">|&gt;</span> Observable.subscribe (<span class="keyword">fun</span> _ <span class="operator">-&gt;</span> <span class="built_in">printfn</span> <span class="string">&quot;FizzBuzz&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">//print events from the nonSimultaneous streams</span></span><br><span class="line">fizzStream</span><br><span class="line"><span class="operator">|&gt;</span> Observable.subscribe (<span class="keyword">fun</span> _ <span class="operator">-&gt;</span> <span class="built_in">printfn</span> <span class="string">&quot;Fizz&quot;</span>)</span><br><span class="line"></span><br><span class="line">buzzStream</span><br><span class="line"><span class="operator">|&gt;</span> Observable.subscribe (<span class="keyword">fun</span> _ <span class="operator">-&gt;</span> <span class="built_in">printfn</span> <span class="string">&quot;Buzz&quot;</span>)</span><br></pre></td></tr></table></figure>
+
+<p>Let’s test it:</p>
+<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// run the two timers at the same time</span></span><br><span class="line">[timer3;timer5]</span><br><span class="line"><span class="operator">|&gt;</span> Async.Parallel</span><br><span class="line"><span class="operator">|&gt;</span> Async.RunSynchronously</span><br></pre></td></tr></table></figure>
+
+<p>Here’s all the code in one complete set:</p>
+<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// create the event streams and raw observables</span></span><br><span class="line"><span class="keyword">let</span> timer3, timerEventStream3 <span class="operator">=</span> createTimerAndObservable <span class="number">300</span></span><br><span class="line"><span class="keyword">let</span> timer5, timerEventStream5 <span class="operator">=</span> createTimerAndObservable <span class="number">500</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// convert the time events into FizzBuzz events with the appropriate id</span></span><br><span class="line"><span class="keyword">let</span> eventStream3 <span class="operator">=</span> timerEventStream3</span><br><span class="line"> <span class="operator">|&gt;</span> Observable.map (<span class="keyword">fun</span> _ <span class="operator">-&gt;</span> &#123;label<span class="operator">=</span><span class="number">3</span>; time<span class="operator">=</span>DateTime.Now&#125;)</span><br><span class="line"><span class="keyword">let</span> eventStream5 <span class="operator">=</span> timerEventStream5</span><br><span class="line"> <span class="operator">|&gt;</span> Observable.map (<span class="keyword">fun</span> _ <span class="operator">-&gt;</span> &#123;label<span class="operator">=</span><span class="number">5</span>; time<span class="operator">=</span>DateTime.Now&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// combine the two streams</span></span><br><span class="line"><span class="keyword">let</span> combinedStream <span class="operator">=</span></span><br><span class="line"> Observable.merge eventStream3 eventStream5</span><br><span class="line"></span><br><span class="line"><span class="comment">// make pairs of events</span></span><br><span class="line"><span class="keyword">let</span> pairwiseStream <span class="operator">=</span></span><br><span class="line"> combinedStream <span class="operator">|&gt;</span> Observable.pairwise</span><br><span class="line"></span><br><span class="line"><span class="comment">// split the stream based on whether the pairs are simultaneous</span></span><br><span class="line"><span class="keyword">let</span> simultaneousStream, nonSimultaneousStream <span class="operator">=</span></span><br><span class="line"> pairwiseStream <span class="operator">|&gt;</span> Observable.partition areSimultaneous</span><br><span class="line"></span><br><span class="line"><span class="comment">// split the non-simultaneous stream based on the id</span></span><br><span class="line"><span class="keyword">let</span> fizzStream, buzzStream <span class="operator">=</span></span><br><span class="line"> nonSimultaneousStream</span><br><span class="line"> <span class="comment">// convert pair of events to the first event</span></span><br><span class="line"> <span class="operator">|&gt;</span> Observable.map (<span class="keyword">fun</span> (ev1,_) <span class="operator">-&gt;</span> ev1)</span><br><span class="line"> <span class="comment">// split on whether the event id is three</span></span><br><span class="line"> <span class="operator">|&gt;</span> Observable.partition (<span class="keyword">fun</span> &#123;label<span class="operator">=</span><span class="built_in">id</span>&#125; <span class="operator">-&gt;</span> <span class="built_in">id</span><span class="operator">=</span><span class="number">3</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">//print events from the combinedStream</span></span><br><span class="line">combinedStream</span><br><span class="line"><span class="operator">|&gt;</span> Observable.subscribe (<span class="keyword">fun</span> &#123;label<span class="operator">=</span><span class="built_in">id</span>;time<span class="operator">=</span>t&#125; <span class="operator">-&gt;</span></span><br><span class="line"> <span class="built_in">printf</span> <span class="string">&quot;[%i] %i.%03i &quot;</span> <span class="built_in">id</span> t.Second t.Millisecond)</span><br><span class="line"></span><br><span class="line"><span class="comment">//print events from the simultaneous stream</span></span><br><span class="line">simultaneousStream</span><br><span class="line"><span class="operator">|&gt;</span> Observable.subscribe (<span class="keyword">fun</span> _ <span class="operator">-&gt;</span> <span class="built_in">printfn</span> <span class="string">&quot;FizzBuzz&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">//print events from the nonSimultaneous streams</span></span><br><span class="line">fizzStream</span><br><span class="line"><span class="operator">|&gt;</span> Observable.subscribe (<span class="keyword">fun</span> _ <span class="operator">-&gt;</span> <span class="built_in">printfn</span> <span class="string">&quot;Fizz&quot;</span>)</span><br><span class="line"></span><br><span class="line">buzzStream</span><br><span class="line"><span class="operator">|&gt;</span> Observable.subscribe (<span class="keyword">fun</span> _ <span class="operator">-&gt;</span> <span class="built_in">printfn</span> <span class="string">&quot;Buzz&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// run the two timers at the same time</span></span><br><span class="line">[timer3;timer5]</span><br><span class="line"><span class="operator">|&gt;</span> Async.Parallel</span><br><span class="line"><span class="operator">|&gt;</span> Async.RunSynchronously</span><br></pre></td></tr></table></figure>
+
+<p>The code might seem a bit long winded, but this kind of incremental, step-wise approach is very clear and self-documenting.</p>
+<p>Some of the benefits of this style are:</p>
+<ul>
+<li>I can see that it meets the requirements just by looking at it, without even running it. Not so with the imperative version.</li>
+<li>From a design point of view, each final “output” stream follows the single responsibility principle – it only does one thing – so it is very easy to associate behavior with it.</li>
+<li>This code has no conditionals, no mutable state, no edge cases. It would be easy to maintain or change, I hope.</li>
+<li>It is easy to debug. For example, I could easily “tap” the output of the <code>simultaneousStream</code> to see if it contains what I think it contains:</li>
+</ul>
+<figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// debugging code</span></span><br><span class="line"><span class="comment">//simultaneousStream |&gt; Observable.subscribe (fun e -&gt; printfn &quot;sim %A&quot; e)</span></span><br><span class="line"><span class="comment">//nonSimultaneousStream |&gt; Observable.subscribe (fun e -&gt; printfn &quot;non-sim %A&quot; e)</span></span><br></pre></td></tr></table></figure>
+
+<p>This would be much harder in the imperative version.</p>
+<h2 id="Summary"><a href="#Summary" class="headerlink" title="Summary"></a>Summary</h2><p>Functional Reactive Programming (known as FRP) is a big topic, and we’ve only just touched on it here. I hope this introduction has given you a glimpse of the usefulness of this way of doing things.</p>
+<p>If you want to learn more, see the documentation for the F# <a target="_blank" rel="noopener" href="https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/ee370313%28v=vs.100%29?redirectedfrom=MSDN">Observable module</a>, which has the basic transformations used above. And there is also the <a target="_blank" rel="noopener" href="https://docs.microsoft.com/en-us/previous-versions/dotnet/reactive-extensions/hh242985%28v=vs.103%29">Reactive Extensions (Rx)</a> library which shipped as part of .NET 4. That contains many other additional transformations.</p>
+
+</div>
+
+<script>
+ window.onload = detectors();
+</script>
+ <div class="post-footer">
+ <div class="h-line-primary"></div>
+ <nav class="post-nav">
+ <div class="prev-item">
+
+ </div>
+ <div class="next-item">
+
+ <div class="icon arrow-right"></div>
+ <div class="post-link">
+ <a href="/2024/09/12/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E5%8D%81%E4%BA%8C%E6%97%A5/">Next</a>
+ </div>
+
+ </div>
+ </nav>
+</div>
+
+
+ <div class="post-comment">
+
+
+
+
+
+
+
+</div>
+
+
+</article>
+ </div>
+ </div>
+
+ <div class="footer">
+ <div class="flex-container">
+ <div class="footer-text">
+
+
+ |
+
+
+ 希望路过的人可以添点柴火让这里暖和点
+
+ </div>
+ </div>
+</div>
+
+ </div>
+
+
+ <div class="search-popup">
+ <div class="search-popup-overlay">
+ </div>
+ <div class="search-popup-window" >
+ <div class="search-header">
+ <div class="search-input-container">
+ <input autocomplete="off" autocapitalize="off" maxlength="80"
+ placeholder="Search Anything" spellcheck="false"
+ type="search" class="search-input">
+ </div>
+ <div class="search-close-btn">
+ <div class="icon close-btn"></div>
+ </div>
+ </div>
+ <div class="search-result-container">
+ </div>
+ </div>
+</div>
+
+<script>
+ const searchConfig = {
+ path : "/search.xml",
+ top_n_per_article: "1",
+ unescape : "false",
+ trigger: "auto",
+ preload: "false"
+ }
+</script>
+<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/search.js"></script>
+<script src="/js/search.js"></script>
+
+
+
+ </body>
+</html>
diff --git a/2024/09/12/二〇二四年九月十二日/index.html b/2024/09/12/二〇二四年九月十二日/index.html
index e41c8259..6a044c38 100644
--- a/2024/09/12/二〇二四年九月十二日/index.html
+++ b/2024/09/12/二〇二四年九月十二日/index.html
@@ -174,7 +174,7 @@
<div class="icon arrow-left"></div>
<div class="post-link">
- <a href="/2024/09/12/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E4%B8%80%E6%97%A5/">Prev</a>
+ <a href="/2024/09/12/Functional-Reactive-Programming-in-F/">Prev</a>
</div>
</div>
diff --git a/archives/2024/09/index.html b/archives/2024/09/index.html
index b1835d06..60227b4d 100644
--- a/archives/2024/09/index.html
+++ b/archives/2024/09/index.html
@@ -144,9 +144,9 @@
<div class="post-list-item">
<div class="post-title">
- <a href="/2024/09/12/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E4%B8%80%E6%97%A5/">
+ <a href="/2024/09/12/Functional-Reactive-Programming-in-F/">
- 二〇二四年九月一日
+ Functional Reactive Programming in F#
</a>
</div>
@@ -216,6 +216,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2024/09/01/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E4%B8%80%E6%97%A5/">
+
+ 二〇二四年九月一日
+
+ </a>
+ </div>
+
+ <span class="post-date">Sep 1, 2024</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2024/09/01/%E8%97%BF%E9%A6%99%E6%AD%A3%E6%B0%94%E6%B0%B4%E5%92%8C%E8%97%BF%E9%A6%99%E6%AD%A3%E6%B0%94%E5%8F%A3%E6%9C%8D%E6%B6%B2%E7%9A%84%E5%8C%BA%E5%88%AB/">
藿香正气水和藿香正气口服液的区别
diff --git a/archives/2024/index.html b/archives/2024/index.html
index 1647fe9c..cafcdfa3 100644
--- a/archives/2024/index.html
+++ b/archives/2024/index.html
@@ -144,9 +144,9 @@
<div class="post-list-item">
<div class="post-title">
- <a href="/2024/09/12/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E4%B8%80%E6%97%A5/">
+ <a href="/2024/09/12/Functional-Reactive-Programming-in-F/">
- 二〇二四年九月一日
+ Functional Reactive Programming in F#
</a>
</div>
@@ -216,9 +216,9 @@
<div class="post-list-item">
<div class="post-title">
- <a href="/2024/09/01/%E8%97%BF%E9%A6%99%E6%AD%A3%E6%B0%94%E6%B0%B4%E5%92%8C%E8%97%BF%E9%A6%99%E6%AD%A3%E6%B0%94%E5%8F%A3%E6%9C%8D%E6%B6%B2%E7%9A%84%E5%8C%BA%E5%88%AB/">
+ <a href="/2024/09/01/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E4%B8%80%E6%97%A5/">
- 藿香正气水和藿香正气口服液的区别
+ 二〇二四年九月一日
</a>
</div>
@@ -234,14 +234,14 @@
<div class="post-list-item">
<div class="post-title">
- <a href="/2024/08/12/%E7%9B%B4%E7%B3%BB%E8%A1%80%E4%BA%B2%E4%B9%8B%E9%97%B4%E4%B8%8D%E8%83%BD%E7%9B%B4%E6%8E%A5%E8%BE%93%E8%A1%80/">
+ <a href="/2024/09/01/%E8%97%BF%E9%A6%99%E6%AD%A3%E6%B0%94%E6%B0%B4%E5%92%8C%E8%97%BF%E9%A6%99%E6%AD%A3%E6%B0%94%E5%8F%A3%E6%9C%8D%E6%B6%B2%E7%9A%84%E5%8C%BA%E5%88%AB/">
- 直系血亲之间不能直接输血
+ 藿香正气水和藿香正气口服液的区别
</a>
</div>
- <span class="post-date">Aug 12, 2024</span>
+ <span class="post-date">Sep 1, 2024</span>
</div>
@@ -252,14 +252,14 @@
<div class="post-list-item">
<div class="post-title">
- <a href="/2024/08/02/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E5%85%AB%E6%9C%88%E4%B8%80%E6%97%A5/">
+ <a href="/2024/08/12/%E7%9B%B4%E7%B3%BB%E8%A1%80%E4%BA%B2%E4%B9%8B%E9%97%B4%E4%B8%8D%E8%83%BD%E7%9B%B4%E6%8E%A5%E8%BE%93%E8%A1%80/">
- 二〇二四年八月一日
+ 直系血亲之间不能直接输血
</a>
</div>
- <span class="post-date">Aug 2, 2024</span>
+ <span class="post-date">Aug 12, 2024</span>
</div>
@@ -270,14 +270,14 @@
<div class="post-list-item">
<div class="post-title">
- <a href="/2024/07/23/OCaml-News-2024-6/">
+ <a href="/2024/08/02/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E5%85%AB%E6%9C%88%E4%B8%80%E6%97%A5/">
- OCaml News 2024-6
+ 二〇二四年八月一日
</a>
</div>
- <span class="post-date">Jul 23, 2024</span>
+ <span class="post-date">Aug 2, 2024</span>
</div>
@@ -288,14 +288,14 @@
<div class="post-list-item">
<div class="post-title">
- <a href="/2024/07/09/%E7%A5%9E%E7%BB%8F%E7%B3%BB%E7%BB%9F%E9%87%8D%E7%82%B9/">
+ <a href="/2024/07/23/OCaml-News-2024-6/">
- 内护神经系统重点考点
+ OCaml News 2024-6
</a>
</div>
- <span class="post-date">Jul 9, 2024</span>
+ <span class="post-date">Jul 23, 2024</span>
</div>
@@ -306,14 +306,14 @@
<div class="post-list-item">
<div class="post-title">
- <a href="/2024/07/01/OCaml-News-2024-4/">
+ <a href="/2024/07/09/%E7%A5%9E%E7%BB%8F%E7%B3%BB%E7%BB%9F%E9%87%8D%E7%82%B9/">
- OCaml News 2024-4
+ 内护神经系统重点考点
</a>
</div>
- <span class="post-date">Jul 1, 2024</span>
+ <span class="post-date">Jul 9, 2024</span>
</div>
@@ -322,7 +322,7 @@
<div class=paginator>
<div class="h-line-primary"></div>
<nav class="pagination">
- <span class="page-number current">1</span><a class="page-number" href="/archives/2024/page/2/">2</a><a class="page-number" href="/archives/2024/page/3/">3</a>
+ <span class="page-number current">1</span><a class="page-number" href="/archives/2024/page/2/">2</a><span class="space">&hellip;</span><a class="page-number" href="/archives/2024/page/4/">4</a>
</nav>
</div>
diff --git a/archives/2024/page/2/index.html b/archives/2024/page/2/index.html
index 11a92517..0dc338e1 100644
--- a/archives/2024/page/2/index.html
+++ b/archives/2024/page/2/index.html
@@ -144,6 +144,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2024/07/01/OCaml-News-2024-4/">
+
+ OCaml News 2024-4
+
+ </a>
+ </div>
+
+ <span class="post-date">Jul 1, 2024</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2024/06/27/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E5%85%AD%E6%9C%88%E4%BA%8C%E5%8D%81%E4%B8%83%E6%97%A5/">
二〇二四年六月二十七日
@@ -299,30 +317,12 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2024/03/22/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B8%89%E6%9C%88%E4%BA%8C%E5%8D%81%E4%BA%8C%E6%97%A5/">
-
- 二〇二四年三月二十二日
-
- </a>
- </div>
-
- <span class="post-date">Mar 22, 2024</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
<div class="h-line-primary"></div>
<nav class="pagination">
- <a class="page-number" href="/archives/2024/">1</a><span class="page-number current">2</span><a class="page-number" href="/archives/2024/page/3/">3</a>
+ <a class="page-number" href="/archives/2024/">1</a><span class="page-number current">2</span><a class="page-number" href="/archives/2024/page/3/">3</a><a class="page-number" href="/archives/2024/page/4/">4</a>
</nav>
</div>
diff --git a/archives/2024/page/3/index.html b/archives/2024/page/3/index.html
index 48ebcd97..68754e76 100644
--- a/archives/2024/page/3/index.html
+++ b/archives/2024/page/3/index.html
@@ -144,6 +144,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2024/03/22/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B8%89%E6%9C%88%E4%BA%8C%E5%8D%81%E4%BA%8C%E6%97%A5/">
+
+ 二〇二四年三月二十二日
+
+ </a>
+ </div>
+
+ <span class="post-date">Mar 22, 2024</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2024/03/12/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B8%89%E6%9C%88%E5%8D%81%E4%BA%8C%E6%97%A5/">
二〇二四年三月十二日
@@ -299,30 +317,12 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2024/01/08/OCaml-News-2024-1/">
-
- OCaml News 2024-1
-
- </a>
- </div>
-
- <span class="post-date">Jan 8, 2024</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
<div class="h-line-primary"></div>
<nav class="pagination">
- <a class="page-number" href="/archives/2024/">1</a><a class="page-number" href="/archives/2024/page/2/">2</a><span class="page-number current">3</span>
+ <a class="page-number" href="/archives/2024/">1</a><a class="page-number" href="/archives/2024/page/2/">2</a><span class="page-number current">3</span><a class="page-number" href="/archives/2024/page/4/">4</a>
</nav>
</div>
diff --git a/archives/2024/page/4/index.html b/archives/2024/page/4/index.html
new file mode 100644
index 00000000..2fc65e50
--- /dev/null
+++ b/archives/2024/page/4/index.html
@@ -0,0 +1,224 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+<meta name="viewport"
+ content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+<meta http-equiv="X-UA-Compatible" content="ie=edge">
+
+ <meta name="author" content="韩暮秋">
+
+
+ <meta name="subtitle" content="暮秋小屋">
+
+
+ <meta name="description" content="这里是暮秋小屋,思念和灵感的寄存处">
+
+
+ <meta name="keywords" content="韩暮秋,MuqiuHan,'Muqiu Han', 'muqiu han', muqiuhan">
+
+
+
+
+<title>Archive: 2024 | 暮秋小屋</title>
+
+
+
+ <link rel="icon" href="/favicon.ico">
+
+
+
+<style>
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;700&family=Roboto+Mono&display=swap');
+</style>
+
+
+
+ <!-- stylesheets list from _config.yml -->
+
+ <link rel="stylesheet" href="/css/style.css">
+
+
+
+
+
+ <!-- scripts list from _config.yml -->
+
+ <script src="/js/frame.js"></script>
+
+
+
+
+
+ <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
+ <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
+
+
+
+
+
+
+
+ <meta name="generator" content="Hexo 6.3.0"></head>
+ <body>
+ <div class="mask-border">
+ </div>
+
+ <div class="wrapper">
+
+ <div class="header">
+ <div class="flex-container">
+ <div class="header-inner">
+ <div class="site-brand-container">
+ <a href="/">
+
+ 暮秋小屋
+
+ </a>
+ </div>
+ <div id="menu-btn" class="menu-btn" onclick="toggleMenu()">
+ Menu
+ </div>
+ <nav class="site-nav">
+ <ul class="menu-list">
+
+
+ <li class="menu-item">
+ <a href="/">主页</a>
+ </li>
+
+
+
+ <li class="menu-item">
+ <a href="/categories/gallery/">日记本</a>
+ </li>
+
+
+
+ <li class="menu-item">
+ <a href="/tags/Medicine/">医学</a>
+ </li>
+
+
+
+ <li class="menu-item">
+ <a href="/tags/Technique/">计算机科学</a>
+ </li>
+
+
+
+ <li class="menu-item">
+ <a href="/tags/Life/">生活</a>
+ </li>
+
+
+
+ <li class="menu-item">
+ <a href="/about">关于</a>
+ </li>
+
+
+
+ <li class="menu-item search-btn">
+ <a href="#">Search</a>
+ </li>
+
+ </ul>
+ </nav>
+ </div>
+ </div>
+</div>
+
+
+ <div class="main">
+ <div class="flex-container">
+ <div class="post-list">
+
+
+
+
+
+
+ <div class="year-title">2024</div>
+
+
+ <div class="post-list-item">
+ <div class="post-title">
+ <a href="/2024/01/08/OCaml-News-2024-1/">
+
+ OCaml News 2024-1
+
+ </a>
+ </div>
+
+ <span class="post-date">Jan 8, 2024</span>
+ </div>
+
+
+ <div id="paginator">
+
+ <div class=paginator>
+ <div class="h-line-primary"></div>
+ <nav class="pagination">
+ <a class="page-number" href="/archives/2024/">1</a><span class="space">&hellip;</span><a class="page-number" href="/archives/2024/page/3/">3</a><span class="page-number current">4</span>
+ </nav>
+ </div>
+
+ </div>
+</div>
+
+ </div>
+ </div>
+
+ <div class="footer">
+ <div class="flex-container">
+ <div class="footer-text">
+
+
+ |
+
+
+ 希望路过的人可以添点柴火让这里暖和点
+
+ </div>
+ </div>
+</div>
+
+ </div>
+
+
+ <div class="search-popup">
+ <div class="search-popup-overlay">
+ </div>
+ <div class="search-popup-window" >
+ <div class="search-header">
+ <div class="search-input-container">
+ <input autocomplete="off" autocapitalize="off" maxlength="80"
+ placeholder="Search Anything" spellcheck="false"
+ type="search" class="search-input">
+ </div>
+ <div class="search-close-btn">
+ <div class="icon close-btn"></div>
+ </div>
+ </div>
+ <div class="search-result-container">
+ </div>
+ </div>
+</div>
+
+<script>
+ const searchConfig = {
+ path : "/search.xml",
+ top_n_per_article: "1",
+ unescape : "false",
+ trigger: "auto",
+ preload: "false"
+ }
+</script>
+<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/search.js"></script>
+<script src="/js/search.js"></script>
+
+
+
+ </body>
+</html>
diff --git a/archives/index.html b/archives/index.html
index f2bad078..3597c87a 100644
--- a/archives/index.html
+++ b/archives/index.html
@@ -144,9 +144,9 @@
<div class="post-list-item">
<div class="post-title">
- <a href="/2024/09/12/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E4%B8%80%E6%97%A5/">
+ <a href="/2024/09/12/Functional-Reactive-Programming-in-F/">
- 二〇二四年九月一日
+ Functional Reactive Programming in F#
</a>
</div>
@@ -216,9 +216,9 @@
<div class="post-list-item">
<div class="post-title">
- <a href="/2024/09/01/%E8%97%BF%E9%A6%99%E6%AD%A3%E6%B0%94%E6%B0%B4%E5%92%8C%E8%97%BF%E9%A6%99%E6%AD%A3%E6%B0%94%E5%8F%A3%E6%9C%8D%E6%B6%B2%E7%9A%84%E5%8C%BA%E5%88%AB/">
+ <a href="/2024/09/01/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E4%B8%80%E6%97%A5/">
- 藿香正气水和藿香正气口服液的区别
+ 二〇二四年九月一日
</a>
</div>
@@ -234,14 +234,14 @@
<div class="post-list-item">
<div class="post-title">
- <a href="/2024/08/12/%E7%9B%B4%E7%B3%BB%E8%A1%80%E4%BA%B2%E4%B9%8B%E9%97%B4%E4%B8%8D%E8%83%BD%E7%9B%B4%E6%8E%A5%E8%BE%93%E8%A1%80/">
+ <a href="/2024/09/01/%E8%97%BF%E9%A6%99%E6%AD%A3%E6%B0%94%E6%B0%B4%E5%92%8C%E8%97%BF%E9%A6%99%E6%AD%A3%E6%B0%94%E5%8F%A3%E6%9C%8D%E6%B6%B2%E7%9A%84%E5%8C%BA%E5%88%AB/">
- 直系血亲之间不能直接输血
+ 藿香正气水和藿香正气口服液的区别
</a>
</div>
- <span class="post-date">Aug 12, 2024</span>
+ <span class="post-date">Sep 1, 2024</span>
</div>
@@ -252,14 +252,14 @@
<div class="post-list-item">
<div class="post-title">
- <a href="/2024/08/02/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E5%85%AB%E6%9C%88%E4%B8%80%E6%97%A5/">
+ <a href="/2024/08/12/%E7%9B%B4%E7%B3%BB%E8%A1%80%E4%BA%B2%E4%B9%8B%E9%97%B4%E4%B8%8D%E8%83%BD%E7%9B%B4%E6%8E%A5%E8%BE%93%E8%A1%80/">
- 二〇二四年八月一日
+ 直系血亲之间不能直接输血
</a>
</div>
- <span class="post-date">Aug 2, 2024</span>
+ <span class="post-date">Aug 12, 2024</span>
</div>
@@ -270,14 +270,14 @@
<div class="post-list-item">
<div class="post-title">
- <a href="/2024/07/23/OCaml-News-2024-6/">
+ <a href="/2024/08/02/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E5%85%AB%E6%9C%88%E4%B8%80%E6%97%A5/">
- OCaml News 2024-6
+ 二〇二四年八月一日
</a>
</div>
- <span class="post-date">Jul 23, 2024</span>
+ <span class="post-date">Aug 2, 2024</span>
</div>
@@ -288,14 +288,14 @@
<div class="post-list-item">
<div class="post-title">
- <a href="/2024/07/09/%E7%A5%9E%E7%BB%8F%E7%B3%BB%E7%BB%9F%E9%87%8D%E7%82%B9/">
+ <a href="/2024/07/23/OCaml-News-2024-6/">
- 内护神经系统重点考点
+ OCaml News 2024-6
</a>
</div>
- <span class="post-date">Jul 9, 2024</span>
+ <span class="post-date">Jul 23, 2024</span>
</div>
@@ -306,14 +306,14 @@
<div class="post-list-item">
<div class="post-title">
- <a href="/2024/07/01/OCaml-News-2024-4/">
+ <a href="/2024/07/09/%E7%A5%9E%E7%BB%8F%E7%B3%BB%E7%BB%9F%E9%87%8D%E7%82%B9/">
- OCaml News 2024-4
+ 内护神经系统重点考点
</a>
</div>
- <span class="post-date">Jul 1, 2024</span>
+ <span class="post-date">Jul 9, 2024</span>
</div>
diff --git a/archives/page/10/index.html b/archives/page/10/index.html
index a32231d5..ca9a1c4d 100644
--- a/archives/page/10/index.html
+++ b/archives/page/10/index.html
@@ -144,6 +144,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2022/03/14/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%BA%8C%E5%B9%B4%E4%B8%89%E6%9C%88%E5%8D%81%E5%9B%9B%E6%97%A5/">
+
+ 二零二二年三月十四日
+
+ </a>
+ </div>
+
+ <span class="post-date">Mar 14, 2022</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2022/01/01/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%BA%8C%E5%B9%B4%E4%B8%80%E6%9C%88%E4%B8%80%E6%97%A5/">
二零二二年一月一日
diff --git a/archives/page/2/index.html b/archives/page/2/index.html
index bcb3a41e..5cd05188 100644
--- a/archives/page/2/index.html
+++ b/archives/page/2/index.html
@@ -144,6 +144,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2024/07/01/OCaml-News-2024-4/">
+
+ OCaml News 2024-4
+
+ </a>
+ </div>
+
+ <span class="post-date">Jul 1, 2024</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2024/06/27/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E5%85%AD%E6%9C%88%E4%BA%8C%E5%8D%81%E4%B8%83%E6%97%A5/">
二〇二四年六月二十七日
@@ -299,24 +317,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2024/03/22/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B8%89%E6%9C%88%E4%BA%8C%E5%8D%81%E4%BA%8C%E6%97%A5/">
-
- 二〇二四年三月二十二日
-
- </a>
- </div>
-
- <span class="post-date">Mar 22, 2024</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/archives/page/3/index.html b/archives/page/3/index.html
index d86de775..fe1960e1 100644
--- a/archives/page/3/index.html
+++ b/archives/page/3/index.html
@@ -144,6 +144,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2024/03/22/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B8%89%E6%9C%88%E4%BA%8C%E5%8D%81%E4%BA%8C%E6%97%A5/">
+
+ 二〇二四年三月二十二日
+
+ </a>
+ </div>
+
+ <span class="post-date">Mar 22, 2024</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2024/03/12/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B8%89%E6%9C%88%E5%8D%81%E4%BA%8C%E6%97%A5/">
二〇二四年三月十二日
@@ -299,24 +317,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2024/01/08/OCaml-News-2024-1/">
-
- OCaml News 2024-1
-
- </a>
- </div>
-
- <span class="post-date">Jan 8, 2024</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/archives/page/4/index.html b/archives/page/4/index.html
index 2141333a..5f9eed35 100644
--- a/archives/page/4/index.html
+++ b/archives/page/4/index.html
@@ -139,6 +139,26 @@
+ <div class="year-title">2024</div>
+
+
+ <div class="post-list-item">
+ <div class="post-title">
+ <a href="/2024/01/08/OCaml-News-2024-1/">
+
+ OCaml News 2024-1
+
+ </a>
+ </div>
+
+ <span class="post-date">Jan 8, 2024</span>
+ </div>
+
+
+
+
+
+
<div class="year-title">2023</div>
@@ -299,24 +319,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2023/10/22/%E5%86%AC%E5%AD%A3%E5%92%B3%E5%97%BD%E8%BE%A8%E5%88%AB%E6%8C%87%E5%8D%97/">
-
- 冬季咳嗽辨别指南
-
- </a>
- </div>
-
- <span class="post-date">Oct 22, 2023</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/archives/page/5/index.html b/archives/page/5/index.html
index ecafa293..a7c821b1 100644
--- a/archives/page/5/index.html
+++ b/archives/page/5/index.html
@@ -144,6 +144,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2023/10/22/%E5%86%AC%E5%AD%A3%E5%92%B3%E5%97%BD%E8%BE%A8%E5%88%AB%E6%8C%87%E5%8D%97/">
+
+ 冬季咳嗽辨别指南
+
+ </a>
+ </div>
+
+ <span class="post-date">Oct 22, 2023</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2023/10/21/OCaml-Core-Int-pow-%E7%9A%84%E5%AE%9E%E7%8E%B0/">
OCaml Core.Int.pow 的实现
@@ -299,24 +317,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2023/08/15/tick-thread%E5%9C%A8Multicore-OCaml%E4%B8%AD%E7%9A%84%E4%BD%9C%E7%94%A8/">
-
- tick thread在Multicore OCaml中的作用
-
- </a>
- </div>
-
- <span class="post-date">Aug 15, 2023</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/archives/page/6/index.html b/archives/page/6/index.html
index dfbeff95..e34488bb 100644
--- a/archives/page/6/index.html
+++ b/archives/page/6/index.html
@@ -144,6 +144,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2023/08/15/tick-thread%E5%9C%A8Multicore-OCaml%E4%B8%AD%E7%9A%84%E4%BD%9C%E7%94%A8/">
+
+ tick thread在Multicore OCaml中的作用
+
+ </a>
+ </div>
+
+ <span class="post-date">Aug 15, 2023</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2023/08/15/poll-error-attribute-in-OCaml/">
使用 [@poll error] 实现线程安全的数据结构
@@ -299,24 +317,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2023/06/10/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%B8%89%E5%B9%B4%E5%85%AD%E6%9C%88%E5%8D%81%E6%97%A5/">
-
- 二零二三年六月十日
-
- </a>
- </div>
-
- <span class="post-date">Jun 10, 2023</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/archives/page/7/index.html b/archives/page/7/index.html
index 628e0ce1..4a6d9d85 100644
--- a/archives/page/7/index.html
+++ b/archives/page/7/index.html
@@ -144,6 +144,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2023/06/10/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%B8%89%E5%B9%B4%E5%85%AD%E6%9C%88%E5%8D%81%E6%97%A5/">
+
+ 二零二三年六月十日
+
+ </a>
+ </div>
+
+ <span class="post-date">Jun 10, 2023</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2023/05/24/Rust-%E9%97%AD%E5%8C%85-lifetime-may-not-live-long-enough-%E9%97%AE%E9%A2%98/">
Rust 闭包 lifetime may not live long enough 问题
@@ -299,24 +317,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2023/04/22/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%B8%89%E5%B9%B4%E5%9B%9B%E6%9C%88%E4%BA%8C%E5%8D%81%E4%BA%8C%E6%97%A5/">
-
- 二零二三年四月二十二日
-
- </a>
- </div>
-
- <span class="post-date">Apr 22, 2023</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/archives/page/8/index.html b/archives/page/8/index.html
index e3071044..fd93b346 100644
--- a/archives/page/8/index.html
+++ b/archives/page/8/index.html
@@ -144,6 +144,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2023/04/22/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%B8%89%E5%B9%B4%E5%9B%9B%E6%9C%88%E4%BA%8C%E5%8D%81%E4%BA%8C%E6%97%A5/">
+
+ 二零二三年四月二十二日
+
+ </a>
+ </div>
+
+ <span class="post-date">Apr 22, 2023</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2023/03/14/%E8%9E%BA%E5%86%85%E9%85%AF%E7%89%87/">
螺内酯片
@@ -299,24 +317,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2023/02/24/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%B8%89%E5%B9%B4%E4%BA%8C%E6%9C%88%E4%BA%8C%E5%8D%81%E5%9B%9B%E6%97%A5/">
-
- 二零二三年二月二十四日
-
- </a>
- </div>
-
- <span class="post-date">Feb 24, 2023</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/archives/page/9/index.html b/archives/page/9/index.html
index d419dcf8..46973296 100644
--- a/archives/page/9/index.html
+++ b/archives/page/9/index.html
@@ -144,6 +144,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2023/02/24/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%B8%89%E5%B9%B4%E4%BA%8C%E6%9C%88%E4%BA%8C%E5%8D%81%E5%9B%9B%E6%97%A5/">
+
+ 二零二三年二月二十四日
+
+ </a>
+ </div>
+
+ <span class="post-date">Feb 24, 2023</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2023/02/22/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%B8%89%E5%B9%B4%E4%BA%8C%E6%9C%88%E4%BA%8C%E5%8D%81%E4%BA%8C%E6%97%A5/">
二零二三年二月二十二日
@@ -301,24 +319,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2022/03/14/%E4%BA%8C%E9%9B%B6%E4%BA%8C%E4%BA%8C%E5%B9%B4%E4%B8%89%E6%9C%88%E5%8D%81%E5%9B%9B%E6%97%A5/">
-
- 二零二二年三月十四日
-
- </a>
- </div>
-
- <span class="post-date">Mar 14, 2022</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/categories/gallery/index.html b/categories/gallery/index.html
index d3aaa106..b1db1046 100644
--- a/categories/gallery/index.html
+++ b/categories/gallery/index.html
@@ -142,9 +142,9 @@
</div>
<div class="post-title">
- <a href="/2024/09/12/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E4%B8%80%E6%97%A5/"</a>
+ <a href="/2024/09/12/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E5%8D%81%E4%BA%8C%E6%97%A5/"</a>
- 二〇二四年九月一日
+ 二〇二四年九月十二日
</a>
</div>
@@ -154,7 +154,7 @@
</div>
<div class="post-img">
- <img src="/img/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E4%B8%80%E6%97%A5.jpg" alt="featured_image">
+ <img src="/img/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E5%8D%81%E4%BA%8C%E6%97%A5.jpg" alt="featured_image">
</div>
</div>
@@ -166,19 +166,19 @@
</div>
<div class="post-title">
- <a href="/2024/09/12/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E5%8D%81%E4%BA%8C%E6%97%A5/"</a>
+ <a href="/2024/09/01/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E4%B8%80%E6%97%A5/"</a>
- 二〇二四年九月十二日
+ 二〇二四年九月一日
</a>
</div>
<span class="post-date">
- Sep 12, 2024
+ Sep 1, 2024
</span>
</div>
<div class="post-img">
- <img src="/img/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E5%8D%81%E4%BA%8C%E6%97%A5.jpg" alt="featured_image">
+ <img src="/img/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E4%B8%80%E6%97%A5.jpg" alt="featured_image">
</div>
</div>
diff --git a/search.xml b/search.xml
index eb265aae..7f70cd01 100644
--- a/search.xml
+++ b/search.xml
@@ -127,6 +127,129 @@
</tags>
</entry>
<entry>
+ <title>Functional Reactive Programming in F#</title>
+ <url>/2024/09/12/Functional-Reactive-Programming-in-F/</url>
+ <content><![CDATA[<p>Events are everywhere. Almost every program has to handle events, whether it be button clicks in the user interface, listening to sockets in a server, or even a system shutdown notification.</p>
+<p>And events are the basis of one of the most common OO design patterns: the “Observer” pattern.</p>
+<p>But as we know, event handling, like concurrency in general, can be tricky to implement. Simple event logic is straightforward, but what about logic like “do something if two events happen in a row but do something different if only one event happens” or “do something if two events happen at roughly the same time”. And how easy is it to combine these requirements in other, more complex ways?</p>
+<p>Even if you can successfully implement these requirements, the code tends to be spaghetti like and hard to understand, even with the best intentions.</p>
+<p>Is there an approach that can make event handling easier?</p>
+<p>We saw in the previous post on message queues that one of the advantages of that approach was that the requests were “serialized” making it conceptually easier to deal with.</p>
+<p>There is a similar approach that can be used with events. The idea is to turn a series of events into an “event stream”. Event streams then become quite like IEnumerables, and so the obvious next step is to treat them in much the the same way that LINQ handles collections, so that they can be filtered, mapped, split and combined.</p>
+<p>F# has built in support for this model, as well as for the more traditional approach.</p>
+<h2 id="A-simple-event-stream"><a href="#A-simple-event-stream" class="headerlink" title="A simple event stream"></a>A simple event stream</h2><p>Let’s start with a simple example to compare the two approaches. We’ll implement the classic event handler approach first.</p>
+<p>First, we define a utility function that will:</p>
+<ul>
+<li>create a timer</li>
+<li>register a handler for the <code>Elapsed</code> event</li>
+<li>run the timer for five seconds and then stop it</li>
+</ul>
+<p>Here’s the code:</p>
+<figure class="highlight fsharp"><table><tr><td class="code"><pre><span class="line"><span class="keyword">open</span> System</span><br><span class="line"><span class="keyword">open</span> System.Threading</span><br><span class="line"></span><br><span class="line"><span class="comment">/// create a timer and register an event handler,</span></span><br><span class="line"><span class="comment">/// then run the timer for five seconds</span></span><br><span class="line"><span class="keyword">let</span> createTimer timerInterval eventHandler <span class="operator">=</span></span><br><span class="line"> <span class="comment">// setup a timer</span></span><br><span class="line"> <span class="keyword">let</span> timer <span class="operator">=</span> <span class="keyword">new</span> System.Timers.Timer(float timerInterval)</span><br><span class="line"> timer.AutoReset <span class="operator">&lt;-</span> <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// add an event handler</span></span><br><span class="line"> timer.Elapsed.Add eventHandler</span><br><span class="line"></span><br><span class="line"> <span class="comment">// return an async task</span></span><br><span class="line"> <span class="keyword">async</span> &#123;</span><br><span class="line"> <span class="comment">// start timer...</span></span><br><span class="line"> timer.Start()</span><br><span class="line"> <span class="comment">// ...run for five seconds...</span></span><br><span class="line"> <span class="keyword">do!</span> Async.Sleep <span class="number">5000</span></span><br><span class="line"> <span class="comment">// ... and stop</span></span><br><span class="line"> timer.Stop()</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure>
+
+<p>Now test it interactively:</p>
+<figure class="highlight fsharp"><table><tr><td class="code"><pre><span class="line"><span class="comment">// create a handler. The event args are ignored</span></span><br><span class="line"><span class="keyword">let</span> basicHandler _ <span class="operator">=</span> <span class="built_in">printfn</span> <span class="string">&quot;tick %A&quot;</span> DateTime.Now</span><br><span class="line"></span><br><span class="line"><span class="comment">// register the handler</span></span><br><span class="line"><span class="keyword">let</span> basicTimer1 <span class="operator">=</span> createTimer <span class="number">1000</span> basicHandler</span><br><span class="line"></span><br><span class="line"><span class="comment">// run the task now</span></span><br><span class="line">Async.RunSynchronously basicTimer1</span><br></pre></td></tr></table></figure>
+
+<p>Now let’s create a similar utility method to create a timer, but this time it will return an “observable” as well, which is the stream of events.</p>
+<figure class="highlight fsharp"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> createTimerAndObservable timerInterval <span class="operator">=</span></span><br><span class="line"> <span class="comment">// setup a timer</span></span><br><span class="line"> <span class="keyword">let</span> timer <span class="operator">=</span> <span class="keyword">new</span> System.Timers.Timer(float timerInterval)</span><br><span class="line"> timer.AutoReset <span class="operator">&lt;-</span> <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// events are automatically IObservable</span></span><br><span class="line"> <span class="keyword">let</span> observable <span class="operator">=</span> timer.Elapsed</span><br><span class="line"></span><br><span class="line"> <span class="comment">// return an async task</span></span><br><span class="line"> <span class="keyword">let</span> task <span class="operator">=</span> <span class="keyword">async</span> &#123;</span><br><span class="line"> timer.Start()</span><br><span class="line"> <span class="keyword">do!</span> Async.Sleep <span class="number">5000</span></span><br><span class="line"> timer.Stop()</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// return a async task and the observable</span></span><br><span class="line"> (task,observable)</span><br></pre></td></tr></table></figure>
+
+<p>And again test it interactively:</p>
+<figure class="highlight fsharp"><table><tr><td class="code"><pre><span class="line"><span class="comment">// create the timer and the corresponding observable</span></span><br><span class="line"><span class="keyword">let</span> basicTimer2 , timerEventStream <span class="operator">=</span> createTimerAndObservable <span class="number">1000</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// register that every time something happens on the</span></span><br><span class="line"><span class="comment">// event stream, print the time.</span></span><br><span class="line">timerEventStream</span><br><span class="line"><span class="operator">|&gt;</span> Observable.subscribe (<span class="keyword">fun</span> _ <span class="operator">-&gt;</span> <span class="built_in">printfn</span> <span class="string">&quot;tick %A&quot;</span> DateTime.Now)</span><br><span class="line"></span><br><span class="line"><span class="comment">// run the task now</span></span><br><span class="line">Async.RunSynchronously basicTimer2</span><br></pre></td></tr></table></figure>
+
+<p>The difference is that instead of registering a handler directly with an event, we are “subscribing” to an event stream. Subtly different, and important.</p>
+<h2 id="Counting-events"><a href="#Counting-events" class="headerlink" title="Counting events"></a>Counting events</h2><p>In this next example, we’ll have a slightly more complex requirement:</p>
+<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">Create a timer that ticks every 500ms.</span><br><span class="line">At each tick, print the number of ticks so far and the current time.</span><br></pre></td></tr></table></figure>
+
+<p>To do this in a classic imperative way, we would probably create a class with a mutable counter, as below:</p>
+<figure class="highlight fsharp"><table><tr><td class="code"><pre><span class="line"><span class="keyword">type</span> <span class="title class_">ImperativeTimerCount</span>() <span class="operator">=</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mutable</span> count <span class="operator">=</span> <span class="number">0</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// the event handler. The event args are ignored</span></span><br><span class="line"> <span class="keyword">member</span> this.handleEvent _ <span class="operator">=</span></span><br><span class="line"> count <span class="operator">&lt;-</span> count <span class="operator">+</span> <span class="number">1</span></span><br><span class="line"> <span class="built_in">printfn</span> <span class="string">&quot;timer ticked with count %i&quot;</span> count</span><br></pre></td></tr></table></figure>
+
+<p>We can reuse the utility functions we created earlier to test it:</p>
+<figure class="highlight fsharp"><table><tr><td class="code"><pre><span class="line"><span class="comment">// create a handler class</span></span><br><span class="line"><span class="keyword">let</span> handler <span class="operator">=</span> <span class="keyword">new</span> ImperativeTimerCount()</span><br><span class="line"></span><br><span class="line"><span class="comment">// register the handler method</span></span><br><span class="line"><span class="keyword">let</span> timerCount1 <span class="operator">=</span> createTimer <span class="number">500</span> handler.handleEvent</span><br><span class="line"></span><br><span class="line"><span class="comment">// run the task now</span></span><br><span class="line">Async.RunSynchronously timerCount1</span><br></pre></td></tr></table></figure>
+
+<p>Let’s see how we would do this same thing in a functional way:</p>
+<figure class="highlight fsharp"><table><tr><td class="code"><pre><span class="line"><span class="comment">// create the timer and the corresponding observable</span></span><br><span class="line"><span class="keyword">let</span> timerCount2, timerEventStream <span class="operator">=</span> createTimerAndObservable <span class="number">500</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// set up the transformations on the event stream</span></span><br><span class="line">timerEventStream</span><br><span class="line"><span class="operator">|&gt;</span> Observable.scan (<span class="keyword">fun</span> count _ <span class="operator">-&gt;</span> count <span class="operator">+</span> <span class="number">1</span>) <span class="number">0</span></span><br><span class="line"><span class="operator">|&gt;</span> Observable.subscribe (<span class="keyword">fun</span> count <span class="operator">-&gt;</span> <span class="built_in">printfn</span> <span class="string">&quot;timer ticked with count %i&quot;</span> count)</span><br><span class="line"></span><br><span class="line"><span class="comment">// run the task now</span></span><br><span class="line">Async.RunSynchronously timerCount2</span><br></pre></td></tr></table></figure>
+
+<p>Here we see how you can build up layers of event transformations, just as you do with list transformations in LINQ.</p>
+<p>The first transformation is <code>scan</code>, which accumulates state for each event. It is roughly equivalent to the <code>List.fold</code> function that we have seen used with lists. In this case, the accumulated state is just a counter.</p>
+<p>And then, for each event, the count is printed out.</p>
+<p>Note that in this functional approach, we didn’t have any mutable state, and we didn’t need to create any special classes.</p>
+<h2 id="Merging-multiple-event-streams"><a href="#Merging-multiple-event-streams" class="headerlink" title="Merging multiple event streams"></a>Merging multiple event streams</h2><p>For a final example, we’ll look at merging multiple event streams.</p>
+<p>Let’s make a requirement based on the well-known “FizzBuzz” problem:</p>
+<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">Create two timers, called &#x27;3&#x27; and &#x27;5&#x27;. The &#x27;3&#x27; timer ticks every 300ms and the &#x27;5&#x27; timer ticks</span><br><span class="line">every 500ms.</span><br><span class="line"></span><br><span class="line">Handle the events as follows:</span><br><span class="line">a) for all events, print the id of the time and the time</span><br><span class="line">b) when a tick is simultaneous with a previous tick, print &#x27;FizzBuzz&#x27;</span><br><span class="line">otherwise:</span><br><span class="line">c) when the &#x27;3&#x27; timer ticks on its own, print &#x27;Fizz&#x27;</span><br><span class="line">d) when the &#x27;5&#x27; timer ticks on its own, print &#x27;Buzz&#x27;</span><br></pre></td></tr></table></figure>
+
+<p>First let’s create some code that both implementations can use.</p>
+<p>We’ll want a generic event type that captures the timer id and the time of the tick.</p>
+<figure class="highlight fsharp"><table><tr><td class="code"><pre><span class="line"><span class="keyword">type</span> <span class="title class_">FizzBuzzEvent</span> <span class="operator">=</span> &#123;label<span class="operator">:</span><span class="type">int</span>; time<span class="operator">:</span> DateTime&#125;</span><br></pre></td></tr></table></figure>
+
+<p>And then we need a utility function to see if two events are simultaneous. We’ll be generous and allow a time difference of up to 50ms.</p>
+<figure class="highlight fsharp"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> areSimultaneous (earlierEvent,laterEvent) <span class="operator">=</span></span><br><span class="line"> <span class="keyword">let</span> &#123;label<span class="operator">=</span>_;time<span class="operator">=</span>t1&#125; <span class="operator">=</span> earlierEvent</span><br><span class="line"> <span class="keyword">let</span> &#123;label<span class="operator">=</span>_;time<span class="operator">=</span>t2&#125; <span class="operator">=</span> laterEvent</span><br><span class="line"> t2.Subtract(t1).Milliseconds <span class="operator">&lt;</span> <span class="number">50</span></span><br></pre></td></tr></table></figure>
+
+<p>In the imperative design, we’ll need to keep track of the previous event, so we can compare them. And we’ll need special case code for the first time, when the previous event doesn’t exist</p>
+<figure class="highlight fsharp"><table><tr><td class="code"><pre><span class="line"><span class="keyword">type</span> <span class="title class_">ImperativeFizzBuzzHandler</span>() <span class="operator">=</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> <span class="keyword">mutable</span> previousEvent<span class="operator">:</span> FizzBuzzEvent <span class="type">option</span> <span class="operator">=</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> printEvent thisEvent <span class="operator">=</span></span><br><span class="line"> <span class="keyword">let</span> &#123;label<span class="operator">=</span><span class="built_in">id</span>; time<span class="operator">=</span>t&#125; <span class="operator">=</span> thisEvent</span><br><span class="line"> <span class="built_in">printf</span> <span class="string">&quot;[%i] %i.%03i &quot;</span> <span class="built_in">id</span> t.Second t.Millisecond</span><br><span class="line"> <span class="keyword">let</span> simultaneous <span class="operator">=</span> previousEvent.IsSome <span class="operator">&amp;&amp;</span> areSimultaneous (previousEvent.Value,thisEvent)</span><br><span class="line"> <span class="keyword">if</span> simultaneous <span class="keyword">then</span> <span class="built_in">printfn</span> <span class="string">&quot;FizzBuzz&quot;</span></span><br><span class="line"> <span class="keyword">elif</span> <span class="built_in">id</span> <span class="operator">=</span> <span class="number">3</span> <span class="keyword">then</span> <span class="built_in">printfn</span> <span class="string">&quot;Fizz&quot;</span></span><br><span class="line"> <span class="keyword">elif</span> <span class="built_in">id</span> <span class="operator">=</span> <span class="number">5</span> <span class="keyword">then</span> <span class="built_in">printfn</span> <span class="string">&quot;Buzz&quot;</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">member</span> this.handleEvent3 eventArgs <span class="operator">=</span></span><br><span class="line"> <span class="keyword">let</span> event <span class="operator">=</span> &#123;label<span class="operator">=</span><span class="number">3</span>; time<span class="operator">=</span>DateTime.Now&#125;</span><br><span class="line"> printEvent event</span><br><span class="line"> previousEvent <span class="operator">&lt;-</span> <span class="literal">Some</span> event</span><br><span class="line"></span><br><span class="line"> <span class="keyword">member</span> this.handleEvent5 eventArgs <span class="operator">=</span></span><br><span class="line"> <span class="keyword">let</span> event <span class="operator">=</span> &#123;label<span class="operator">=</span><span class="number">5</span>; time<span class="operator">=</span>DateTime.Now&#125;</span><br><span class="line"> printEvent event</span><br><span class="line"> previousEvent <span class="operator">&lt;-</span> <span class="literal">Some</span> event</span><br></pre></td></tr></table></figure>
+
+<p>Now the code is beginning to get ugly fast! Already we have mutable state, complex conditional logic, and special cases, just for such a simple requirement.</p>
+<p>Let’s test it:</p>
+<figure class="highlight fsharp"><table><tr><td class="code"><pre><span class="line"><span class="comment">// create the class</span></span><br><span class="line"><span class="keyword">let</span> handler <span class="operator">=</span> <span class="keyword">new</span> ImperativeFizzBuzzHandler()</span><br><span class="line"></span><br><span class="line"><span class="comment">// create the two timers and register the two handlers</span></span><br><span class="line"><span class="keyword">let</span> timer3 <span class="operator">=</span> createTimer <span class="number">300</span> handler.handleEvent3</span><br><span class="line"><span class="keyword">let</span> timer5 <span class="operator">=</span> createTimer <span class="number">500</span> handler.handleEvent5</span><br><span class="line"></span><br><span class="line"><span class="comment">// run the two timers at the same time</span></span><br><span class="line">[timer3;timer5]</span><br><span class="line"><span class="operator">|&gt;</span> Async.Parallel</span><br><span class="line"><span class="operator">|&gt;</span> Async.RunSynchronously</span><br></pre></td></tr></table></figure>
+
+<p>It does work, but are you sure the code is not buggy? Are you likely to accidentally break something if you change it?</p>
+<p>The problem with this imperative code is that it has a lot of noise that obscures the the requirements.</p>
+<p>Can the functional version do better? Let’s see!</p>
+<p>First, we create <em>two</em> event streams, one for each timer:</p>
+<figure class="highlight fsharp"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> timer3, timerEventStream3 <span class="operator">=</span> createTimerAndObservable <span class="number">300</span></span><br><span class="line"><span class="keyword">let</span> timer5, timerEventStream5 <span class="operator">=</span> createTimerAndObservable <span class="number">500</span></span><br></pre></td></tr></table></figure>
+
+<p>Next, we convert each event on the “raw” event streams into our FizzBuzz event type:</p>
+<figure class="highlight fsharp"><table><tr><td class="code"><pre><span class="line"><span class="comment">// convert the time events into FizzBuzz events with the appropriate id</span></span><br><span class="line"><span class="keyword">let</span> eventStream3 <span class="operator">=</span></span><br><span class="line"> timerEventStream3</span><br><span class="line"> <span class="operator">|&gt;</span> Observable.map (<span class="keyword">fun</span> _ <span class="operator">-&gt;</span> &#123;label<span class="operator">=</span><span class="number">3</span>; time<span class="operator">=</span>DateTime.Now&#125;)</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> eventStream5 <span class="operator">=</span></span><br><span class="line"> timerEventStream5</span><br><span class="line"> <span class="operator">|&gt;</span> Observable.map (<span class="keyword">fun</span> _ <span class="operator">-&gt;</span> &#123;label<span class="operator">=</span><span class="number">5</span>; time<span class="operator">=</span>DateTime.Now&#125;)</span><br></pre></td></tr></table></figure>
+
+<p>Now, to see if two events are simultaneous, we need to compare them from the two different streams somehow.</p>
+<p>It’s actually easier than it sounds, because we can:</p>
+<ul>
+<li>combine the two streams into a single stream:</li>
+<li>then create pairs of sequential events</li>
+<li>then test the pairs to see if they are simultaneous</li>
+<li>then split the input stream into two new output streams based on that test</li>
+</ul>
+<p>Here’s the actual code to do this:</p>
+<figure class="highlight fsharp"><table><tr><td class="code"><pre><span class="line"><span class="comment">// combine the two streams</span></span><br><span class="line"><span class="keyword">let</span> combinedStream <span class="operator">=</span></span><br><span class="line"> Observable.merge eventStream3 eventStream5</span><br><span class="line"></span><br><span class="line"><span class="comment">// make pairs of events</span></span><br><span class="line"><span class="keyword">let</span> pairwiseStream <span class="operator">=</span></span><br><span class="line"> combinedStream <span class="operator">|&gt;</span> Observable.pairwise</span><br><span class="line"></span><br><span class="line"><span class="comment">// split the stream based on whether the pairs are simultaneous</span></span><br><span class="line"><span class="keyword">let</span> simultaneousStream, nonSimultaneousStream <span class="operator">=</span></span><br><span class="line"> pairwiseStream <span class="operator">|&gt;</span> Observable.partition areSimultaneous</span><br></pre></td></tr></table></figure>
+
+<p>Finally, we can split the <code>nonSimultaneousStream</code> again, based on the event id:</p>
+<figure class="highlight fsharp"><table><tr><td class="code"><pre><span class="line"><span class="comment">// split the non-simultaneous stream based on the id</span></span><br><span class="line"><span class="keyword">let</span> fizzStream, buzzStream <span class="operator">=</span></span><br><span class="line"> nonSimultaneousStream</span><br><span class="line"> <span class="comment">// convert pair of events to the first event</span></span><br><span class="line"> <span class="operator">|&gt;</span> Observable.map (<span class="keyword">fun</span> (ev1,_) <span class="operator">-&gt;</span> ev1)</span><br><span class="line"> <span class="comment">// split on whether the event id is three</span></span><br><span class="line"> <span class="operator">|&gt;</span> Observable.partition (<span class="keyword">fun</span> &#123;label<span class="operator">=</span><span class="built_in">id</span>&#125; <span class="operator">-&gt;</span> <span class="built_in">id</span><span class="operator">=</span><span class="number">3</span>)</span><br></pre></td></tr></table></figure>
+
+<p>Let’s review so far. We have started with the two original event streams and from them created four new ones:</p>
+<ul>
+<li><code>combinedStream</code> contains all the events</li>
+<li><code>simultaneousStream</code> contains only the simultaneous events</li>
+<li><code>fizzStream</code> contains only the non-simultaneous events with id&#x3D;3</li>
+<li><code>buzzStream</code> contains only the non-simultaneous events with id&#x3D;5</li>
+</ul>
+<p>Now all we need to do is attach behavior to each stream:</p>
+<figure class="highlight fsharp"><table><tr><td class="code"><pre><span class="line"><span class="comment">//print events from the combinedStream</span></span><br><span class="line">combinedStream</span><br><span class="line"><span class="operator">|&gt;</span> Observable.subscribe (<span class="keyword">fun</span> &#123;label<span class="operator">=</span><span class="built_in">id</span>;time<span class="operator">=</span>t&#125; <span class="operator">-&gt;</span></span><br><span class="line"> <span class="built_in">printf</span> <span class="string">&quot;[%i] %i.%03i &quot;</span> <span class="built_in">id</span> t.Second t.Millisecond)</span><br><span class="line"></span><br><span class="line"><span class="comment">//print events from the simultaneous stream</span></span><br><span class="line">simultaneousStream</span><br><span class="line"><span class="operator">|&gt;</span> Observable.subscribe (<span class="keyword">fun</span> _ <span class="operator">-&gt;</span> <span class="built_in">printfn</span> <span class="string">&quot;FizzBuzz&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">//print events from the nonSimultaneous streams</span></span><br><span class="line">fizzStream</span><br><span class="line"><span class="operator">|&gt;</span> Observable.subscribe (<span class="keyword">fun</span> _ <span class="operator">-&gt;</span> <span class="built_in">printfn</span> <span class="string">&quot;Fizz&quot;</span>)</span><br><span class="line"></span><br><span class="line">buzzStream</span><br><span class="line"><span class="operator">|&gt;</span> Observable.subscribe (<span class="keyword">fun</span> _ <span class="operator">-&gt;</span> <span class="built_in">printfn</span> <span class="string">&quot;Buzz&quot;</span>)</span><br></pre></td></tr></table></figure>
+
+<p>Let’s test it:</p>
+<figure class="highlight fsharp"><table><tr><td class="code"><pre><span class="line"><span class="comment">// run the two timers at the same time</span></span><br><span class="line">[timer3;timer5]</span><br><span class="line"><span class="operator">|&gt;</span> Async.Parallel</span><br><span class="line"><span class="operator">|&gt;</span> Async.RunSynchronously</span><br></pre></td></tr></table></figure>
+
+<p>Here’s all the code in one complete set:</p>
+<figure class="highlight fsharp"><table><tr><td class="code"><pre><span class="line"><span class="comment">// create the event streams and raw observables</span></span><br><span class="line"><span class="keyword">let</span> timer3, timerEventStream3 <span class="operator">=</span> createTimerAndObservable <span class="number">300</span></span><br><span class="line"><span class="keyword">let</span> timer5, timerEventStream5 <span class="operator">=</span> createTimerAndObservable <span class="number">500</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// convert the time events into FizzBuzz events with the appropriate id</span></span><br><span class="line"><span class="keyword">let</span> eventStream3 <span class="operator">=</span> timerEventStream3</span><br><span class="line"> <span class="operator">|&gt;</span> Observable.map (<span class="keyword">fun</span> _ <span class="operator">-&gt;</span> &#123;label<span class="operator">=</span><span class="number">3</span>; time<span class="operator">=</span>DateTime.Now&#125;)</span><br><span class="line"><span class="keyword">let</span> eventStream5 <span class="operator">=</span> timerEventStream5</span><br><span class="line"> <span class="operator">|&gt;</span> Observable.map (<span class="keyword">fun</span> _ <span class="operator">-&gt;</span> &#123;label<span class="operator">=</span><span class="number">5</span>; time<span class="operator">=</span>DateTime.Now&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// combine the two streams</span></span><br><span class="line"><span class="keyword">let</span> combinedStream <span class="operator">=</span></span><br><span class="line"> Observable.merge eventStream3 eventStream5</span><br><span class="line"></span><br><span class="line"><span class="comment">// make pairs of events</span></span><br><span class="line"><span class="keyword">let</span> pairwiseStream <span class="operator">=</span></span><br><span class="line"> combinedStream <span class="operator">|&gt;</span> Observable.pairwise</span><br><span class="line"></span><br><span class="line"><span class="comment">// split the stream based on whether the pairs are simultaneous</span></span><br><span class="line"><span class="keyword">let</span> simultaneousStream, nonSimultaneousStream <span class="operator">=</span></span><br><span class="line"> pairwiseStream <span class="operator">|&gt;</span> Observable.partition areSimultaneous</span><br><span class="line"></span><br><span class="line"><span class="comment">// split the non-simultaneous stream based on the id</span></span><br><span class="line"><span class="keyword">let</span> fizzStream, buzzStream <span class="operator">=</span></span><br><span class="line"> nonSimultaneousStream</span><br><span class="line"> <span class="comment">// convert pair of events to the first event</span></span><br><span class="line"> <span class="operator">|&gt;</span> Observable.map (<span class="keyword">fun</span> (ev1,_) <span class="operator">-&gt;</span> ev1)</span><br><span class="line"> <span class="comment">// split on whether the event id is three</span></span><br><span class="line"> <span class="operator">|&gt;</span> Observable.partition (<span class="keyword">fun</span> &#123;label<span class="operator">=</span><span class="built_in">id</span>&#125; <span class="operator">-&gt;</span> <span class="built_in">id</span><span class="operator">=</span><span class="number">3</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">//print events from the combinedStream</span></span><br><span class="line">combinedStream</span><br><span class="line"><span class="operator">|&gt;</span> Observable.subscribe (<span class="keyword">fun</span> &#123;label<span class="operator">=</span><span class="built_in">id</span>;time<span class="operator">=</span>t&#125; <span class="operator">-&gt;</span></span><br><span class="line"> <span class="built_in">printf</span> <span class="string">&quot;[%i] %i.%03i &quot;</span> <span class="built_in">id</span> t.Second t.Millisecond)</span><br><span class="line"></span><br><span class="line"><span class="comment">//print events from the simultaneous stream</span></span><br><span class="line">simultaneousStream</span><br><span class="line"><span class="operator">|&gt;</span> Observable.subscribe (<span class="keyword">fun</span> _ <span class="operator">-&gt;</span> <span class="built_in">printfn</span> <span class="string">&quot;FizzBuzz&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">//print events from the nonSimultaneous streams</span></span><br><span class="line">fizzStream</span><br><span class="line"><span class="operator">|&gt;</span> Observable.subscribe (<span class="keyword">fun</span> _ <span class="operator">-&gt;</span> <span class="built_in">printfn</span> <span class="string">&quot;Fizz&quot;</span>)</span><br><span class="line"></span><br><span class="line">buzzStream</span><br><span class="line"><span class="operator">|&gt;</span> Observable.subscribe (<span class="keyword">fun</span> _ <span class="operator">-&gt;</span> <span class="built_in">printfn</span> <span class="string">&quot;Buzz&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// run the two timers at the same time</span></span><br><span class="line">[timer3;timer5]</span><br><span class="line"><span class="operator">|&gt;</span> Async.Parallel</span><br><span class="line"><span class="operator">|&gt;</span> Async.RunSynchronously</span><br></pre></td></tr></table></figure>
+
+<p>The code might seem a bit long winded, but this kind of incremental, step-wise approach is very clear and self-documenting.</p>
+<p>Some of the benefits of this style are:</p>
+<ul>
+<li>I can see that it meets the requirements just by looking at it, without even running it. Not so with the imperative version.</li>
+<li>From a design point of view, each final “output” stream follows the single responsibility principle – it only does one thing – so it is very easy to associate behavior with it.</li>
+<li>This code has no conditionals, no mutable state, no edge cases. It would be easy to maintain or change, I hope.</li>
+<li>It is easy to debug. For example, I could easily “tap” the output of the <code>simultaneousStream</code> to see if it contains what I think it contains:</li>
+</ul>
+<figure class="highlight fsharp"><table><tr><td class="code"><pre><span class="line"><span class="comment">// debugging code</span></span><br><span class="line"><span class="comment">//simultaneousStream |&gt; Observable.subscribe (fun e -&gt; printfn &quot;sim %A&quot; e)</span></span><br><span class="line"><span class="comment">//nonSimultaneousStream |&gt; Observable.subscribe (fun e -&gt; printfn &quot;non-sim %A&quot; e)</span></span><br></pre></td></tr></table></figure>
+
+<p>This would be much harder in the imperative version.</p>
+<h2 id="Summary"><a href="#Summary" class="headerlink" title="Summary"></a>Summary</h2><p>Functional Reactive Programming (known as FRP) is a big topic, and we’ve only just touched on it here. I hope this introduction has given you a glimpse of the usefulness of this way of doing things.</p>
+<p>If you want to learn more, see the documentation for the F# <a href="https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/ee370313%28v=vs.100%29?redirectedfrom=MSDN">Observable module</a>, which has the basic transformations used above. And there is also the <a href="https://docs.microsoft.com/en-us/previous-versions/dotnet/reactive-extensions/hh242985%28v=vs.103%29">Reactive Extensions (Rx)</a> library which shipped as part of .NET 4. That contains many other additional transformations.</p>
+]]></content>
+ <tags>
+ <tag>Technique</tag>
+ <tag>F#</tag>
+ </tags>
+ </entry>
+ <entry>
<title>G-Machine</title>
<url>/2024/01/23/G-Machine/</url>
<content><![CDATA[<ul>
@@ -1278,7 +1401,7 @@
</entry>
<entry>
<title>二〇二四年九月一日</title>
- <url>/2024/09/12/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E4%B8%80%E6%97%A5/</url>
+ <url>/2024/09/01/%E4%BA%8C%E3%80%87%E4%BA%8C%E5%9B%9B%E5%B9%B4%E4%B9%9D%E6%9C%88%E4%B8%80%E6%97%A5/</url>
<content><![CDATA[<p>烦闷的夏,充斥着丰沛雨水的夏,挖了无数个西瓜的夏,牵着她有层薄汗手掌的夏,她还在的夏,终于还是过去了。</p>
<p>眼里噙着的、汇入的、环绕的、裹藏的,产于自身的液体,是加厚的镜片,人们笑着走来打招呼的轮廓亟其模糊,连唇齿语调也跟着散了光线。</p>
<p>不对,等下,我们不能一坐下来铺开纸就谈死亡,我们应该先把天空涂得橙黄,支开笔,喝几口发着陈味的汤。</p>
diff --git a/tags/F/index.html b/tags/F/index.html
index 6f9a8320..2614189d 100644
--- a/tags/F/index.html
+++ b/tags/F/index.html
@@ -151,6 +151,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2024/09/12/Functional-Reactive-Programming-in-F/">
+
+ Functional Reactive Programming in F#
+
+ </a>
+ </div>
+
+ <span class="post-date">Sep 12, 2024</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2024/06/06/NET-AOT-%E4%B8%8B%E7%9A%84-F-%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90%E5%BA%93%E9%80%89%E6%8B%A9/">
.NET AOT 下的 F# 命令行参数解析库选择
diff --git a/tags/Technique/index.html b/tags/Technique/index.html
index c6d34c15..6f91cbd5 100644
--- a/tags/Technique/index.html
+++ b/tags/Technique/index.html
@@ -151,6 +151,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2024/09/12/Functional-Reactive-Programming-in-F/">
+
+ Functional Reactive Programming in F#
+
+ </a>
+ </div>
+
+ <span class="post-date">Sep 12, 2024</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2024/07/23/OCaml-News-2024-6/">
OCaml News 2024-6
@@ -306,24 +324,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2024/01/08/OCaml-News-2024-1/">
-
- OCaml News 2024-1
-
- </a>
- </div>
-
- <span class="post-date">Jan 8, 2024</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/tags/Technique/page/2/index.html b/tags/Technique/page/2/index.html
index 573fb838..0177dd5c 100644
--- a/tags/Technique/page/2/index.html
+++ b/tags/Technique/page/2/index.html
@@ -146,6 +146,26 @@
+ <div class="year-title">2024</div>
+
+
+ <div class="post-list-item">
+ <div class="post-title">
+ <a href="/2024/01/08/OCaml-News-2024-1/">
+
+ OCaml News 2024-1
+
+ </a>
+ </div>
+
+ <span class="post-date">Jan 8, 2024</span>
+ </div>
+
+
+
+
+
+
<div class="year-title">2023</div>
@@ -306,24 +326,6 @@
</div>
-
-
-
-
-
- <div class="post-list-item">
- <div class="post-title">
- <a href="/2023/05/12/C-20-%E5%AE%9E%E7%8E%B0-string-split/">
-
- C++ 20 实现 string split
-
- </a>
- </div>
-
- <span class="post-date">May 12, 2023</span>
- </div>
-
-
<div id="paginator">
<div class=paginator>
diff --git a/tags/Technique/page/3/index.html b/tags/Technique/page/3/index.html
index bb7d4b1b..c0d5395c 100644
--- a/tags/Technique/page/3/index.html
+++ b/tags/Technique/page/3/index.html
@@ -151,6 +151,24 @@
<div class="post-list-item">
<div class="post-title">
+ <a href="/2023/05/12/C-20-%E5%AE%9E%E7%8E%B0-string-split/">
+
+ C++ 20 实现 string split
+
+ </a>
+ </div>
+
+ <span class="post-date">May 12, 2023</span>
+ </div>
+
+
+
+
+
+
+
+ <div class="post-list-item">
+ <div class="post-title">
<a href="/2023/05/11/C-vector-%E7%9A%84-push-back-%E5%92%8C-emplace-back/">
C++ vector 的 push_back 和 emplace_back