301 http code means that the requested resource has been permanently moved to a new URL. All future requests should use the new address.
The browser will automatically redirect the user to the new address, and search engines will update their indexes.
302 http code indicates that the requested resource is temporarily available at a different URL.
The user is automatically redirected to the new URL, but search engines continue to index the old address.
200 http code is a standard successful HTTP server response. It means that the client’s request (e.g., from a browser) was successfully processed, and the server is delivering the requested data.
The user receives content without errors, and the page or application functions properly. If Code 200 is accompanied by data, the browser or program processes and displays it to the user.
GET / HTTP/1.1 Host: vbfox.com Accept: */* User-Agent: Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; [email protected])
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width initial-scale=1" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Blog of a Fox</title> <meta name="description" content="Blog of Julien Roncaglia (aka VirtualBlackFox) "> <link type="application/atom+xml" rel="alternate" href="https://blog.vbfox.net/feed.xml" title="Blog of a Fox" /> <link rel="stylesheet" href="/css/main.css"> <link rel="canonical" href="https://blog.vbfox.net/"> <meta name="theme-color" content="#23272A" /> </head> <body> <header class="site-header"> <h1><a class="page-link" href="/">Blog of a Fox</a></h1> <div class="wrapper"> <nav class="site-nav"> <a href="#" class="menu-icon"> <svg viewBox="0 0 18 15"> <path fill="#424242" d="M18,1.484c0,0.82-0.665,1.484-1.484,1.484H1.484C0.665,2.969,0,2.304,0,1.484l0,0C0,0.665,0.665,0,1.484,0 h15.031C17.335,0,18,0.665,18,1.484L18,1.484z"/> <path fill="#424242" d="M18,7.516C18,8.335,17.335,9,16.516,9H1.484C0.665,9,0,8.335,0,7.516l0,0c0-0.82,0.665-1.484,1.484-1.484 h15.031C17.335,6.031,18,6.696,18,7.516L18,7.516z"/> <path fill="#424242" d="M18,13.516C18,14.335,17.335,15,16.516,15H1.484C0.665,15,0,14.335,0,13.516l0,0 c0-0.82,0.665-1.484,1.484-1.484h15.031C17.335,12.031,18,12.696,18,13.516L18,13.516z"/> </svg> </a> <div class="trigger"> <a class="page-link active" href="/">blog</a> <a class="page-link " href="/archive/">archive</a> <a class="page-link" href="https://www.linkedin.com/in/julienroncaglia/" rel="me" target="_blank">cv</a> <a class="page-link" href="mailto:[email protected]" rel="me"> <span class="icon icon--mail"> <svg viewBox="0 0 40 40"> <path fill="red" stroke="none" d="M28.516,7.167H3.482L15.998999999999999,14.274999999999999L28.516,7.167ZM16.74,17.303C16.51,17.434,16.255,17.5,16,17.5S15.49,17.434,15.259,17.303L2.5,10.06V24.833H29.5V10.06L16.74,17.303Z" transform="matrix(1,0,0,1,4,4)" /> </svg> [email protected] </span> </a> <a class="page-link" href="https://github.com/vbfox" rel="me" target="_blank"> <span class="icon icon--github"> <svg viewBox="0 0 16 16"> <path fill="#828282" d="M7.999,0.431c-4.285,0-7.76,3.474-7.76,7.761 c0,3.428,2.223,6.337,5.307,7.363c0.388,0.071,0.53-0.168,0.53-0.374c0-0.184-0.007-0.672-0.01-1.32 c-2.159,0.469-2.614-1.04-2.614-1.04c-0.353-0.896-0.862-1.135-0.862-1.135c-0.705-0.481,0.053-0.472,0.053-0.472 c0.779,0.055,1.189,0.8,1.189,0.8c0.692,1.186,1.816,0.843,2.258,0.645c0.071-0.502,0.271-0.843,0.493-1.037 C4.86,11.425,3.049,10.76,3.049,7.786c0-0.847,0.302-1.54,0.799-2.082C3.768,5.507,3.501,4.718,3.924,3.65 c0,0,0.652-0.209,2.134,0.796C6.677,4.273,7.34,4.187,8,4.184c0.659,0.003,1.323,0.089,1.943,0.261 c1.482-1.004,2.132-0.796,2.132-0.796c0.423,1.068,0.157,1.857,0.077,2.054c0.497,0.542,0.798,1.235,0.798,2.082 c0,2.981-1.814,3.637-3.543,3.829c0.279,0.24,0.527,0.713,0.527,1.437c0,1.037-0.01,1.874-0.01,2.129 c0,0.208,0.14,0.449,0.534,0.373c3.081-1.028,5.302-3.935,5.302-7.362C15.76,3.906,12.285,0.431,7.999,0.431z"/> </svg> vbfox </span> </a> <a class="page-link" href="https://twitter.com/virtualblackfox" rel="me" target="_blank"> <span class="icon icon--twitter"> <svg viewBox="0 0 16 16"> <path fill="#828282" d="M15.969,3.058c-0.586,0.26-1.217,0.436-1.878,0.515c0.675-0.405,1.194-1.045,1.438-1.809 c-0.632,0.375-1.332,0.647-2.076,0.793c-0.596-0.636-1.446-1.033-2.387-1.033c-1.806,0-3.27,1.464-3.27,3.27 c0,0.256,0.029,0.506,0.085,0.745C5.163,5.404,2.753,4.102,1.14,2.124C0.859,2.607,0.698,3.168,0.698,3.767 c0,1.134,0.577,2.135,1.455,2.722C1.616,6.472,1.112,6.325,0.671,6.08c0,0.014,0,0.027,0,0.041c0,1.584,1.127,2.906,2.623,3.206 C3.02,9.402,2.731,9.442,2.433,9.442c-0.211,0-0.416-0.021-0.615-0.059c0.416,1.299,1.624,2.245,3.055,2.271 c-1.119,0.877-2.529,1.4-4.061,1.4c-0.264,0-0.524-0.015-0.78-0.046c1.447,0.928,3.166,1.469,5.013,1.469 c6.015,0,9.304-4.983,9.304-9.304c0-0.142-0.003-0.283-0.009-0.423C14.976,4.29,15.531,3.714,15.969,3.058z"/> </svg> virtualblackfox </span> </a> <a class="page-link" href="https://hachyderm.io/@vbfox" rel="me" target="_blank"> <span class="icon icon--mastodon"> <svg viewBox="0 0 216.4144 232.00976"> <path fill="#2b90d9" d="M211.80734 139.0875c-3.18125 16.36625-28.4925 34.2775-57.5625 37.74875-15.15875 1.80875-30.08375 3.47125-45.99875 2.74125-26.0275-1.1925-46.565-6.2125-46.565-6.2125 0 2.53375.15625 4.94625.46875 7.2025 3.38375 25.68625 25.47 27.225 46.39125 27.9425 21.11625.7225 39.91875-5.20625 39.91875-5.20625l.8675 19.09s-14.77 7.93125-41.08125 9.39c-14.50875.7975-32.52375-.365-53.50625-5.91875C9.23234 213.82 1.40609 165.31125.20859 116.09125c-.365-14.61375-.14-28.39375-.14-39.91875 0-50.33 32.97625-65.0825 32.97625-65.0825C49.67234 3.45375 78.20359.2425 107.86484 0h.72875c29.66125.2425 58.21125 3.45375 74.8375 11.09 0 0 32.975 14.7525 32.975 65.0825 0 0 .41375 37.13375-4.59875 62.915"/> <path fill="#fff" d="M177.50984 80.077v60.94125h-24.14375v-59.15c0-12.46875-5.24625-18.7975-15.74-18.7975-11.6025 0-17.4175 7.5075-17.4175 22.3525v32.37625H96.20734V85.42325c0-14.845-5.81625-22.3525-17.41875-22.3525-10.49375 0-15.74 6.32875-15.74 18.7975v59.15H38.90484V80.077c0-12.455 3.17125-22.3525 9.54125-29.675 6.56875-7.3225 15.17125-11.07625 25.85-11.07625 12.355 0 21.71125 4.74875 27.8975 14.2475l6.01375 10.08125 6.015-10.08125c6.185-9.49875 15.54125-14.2475 27.8975-14.2475 10.6775 0 19.28 3.75375 25.85 11.07625 6.36875 7.3225 9.54 17.22 9.54 29.675"/> </svg> [email protected] </span> </a> </div> </nav> </div> </header> <div class="page-content"> <div class="wrapper"> <div class="home-page"> <ul class="post-list"> <li class="post"> <div class="post-header"> <h2 class="post-title"> <a class="post-link" href="/2018/09/12/fake-typed-targets.html">Replacing FAKE target strings with types</a> </h2> <span class="post-meta">Sep 12, 2018</span> </div> <article class="post-content"> <p>After years of usage I started to find <a href="https://fake.build/">FAKE</a>’s <code class="language-plaintext highlighter-rouge">Target</code> syntax less expressive than it could be.</p> <p>I developed multiple variants of a “Task” library to replace it that was available via gist, sometimes tweeting about it in answer to discussions but never clearly releasing it.</p> <p>With FAKE 5 moving to small modules it’s the occasion to choose a good name “<code class="language-plaintext highlighter-rouge">BuildTask</code>” and release it on <a href="https://www.nuget.org/packages/BlackFox.Fake.BuildTask/">NuGet</a> with the source code on <a href="https://github.com/vbfox/FoxSharp/tree/master/src/BlackFox.Fake.BuildTask">GitHub</a>.</p> <h2 id="what-does-buildtask-changes">What does BuildTask changes</h2> <h3 id="strongly-typed-targets">Strongly-Typed targets</h3> <p><code class="language-plaintext highlighter-rouge">Target</code> has a very stringly-typed API, strings are used everywhere as identifiers, with the consequence that their value is only verified when the script is executed.</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">Target</span><span class="p">.</span><span class="n">create</span> <span class="s2">"Default"</span> <span class="p">(</span><span class="k">fun</span> <span class="p">_</span> <span class="p">-></span> <span class="c">(* ... *)</span> <span class="p">)</span> <span class="nn">Target</span><span class="p">.</span><span class="n">runOrDefault</span> <span class="s2">"Defautl"</span> </code></pre></div></div> <p>In <code class="language-plaintext highlighter-rouge">BuildTask</code> each created task returns a value that represent it and the compiler check the value usage as with any other variable.</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">defaultTask</span> <span class="p">=</span> <span class="nn">BuildTask</span><span class="p">.</span><span class="n">createFn</span> <span class="s2">"Default"</span> <span class="bp">[]</span> <span class="p">(</span><span class="k">fun</span> <span class="p">_</span> <span class="p">-></span> <span class="c">(* ... *)</span> <span class="p">)</span> <span class="nn">BuildTask</span><span class="p">.</span><span class="n">runOrDefault</span> <span class="n">defaultTask</span> </code></pre></div></div> <h3 id="default-syntax-using-computation-expressions">Default syntax using computation expressions</h3> <p>While FAKE <code class="language-plaintext highlighter-rouge">Target.create</code> expect a function as parameter <code class="language-plaintext highlighter-rouge">BuildTask.create</code> uses a computation expression to do the same (Like <a href="https://github.com/haf/expecto#writing-tests">Expecto</a>), removing a little bit of verbosity for each target:</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">Target</span><span class="p">.</span><span class="n">create</span> <span class="s2">"Default"</span> <span class="p">(</span><span class="k">fun</span> <span class="p">_</span> <span class="p">-></span> <span class="c">(* ... *)</span> <span class="p">)</span> </code></pre></div></div> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">BuildTask</span><span class="p">.</span><span class="n">create</span> <span class="s2">"Default"</span> <span class="bp">[]</span> <span class="p">{</span> <span class="c">(* ... *)</span> <span class="p">}</span> </code></pre></div></div> <p><em>Note: <code class="language-plaintext highlighter-rouge">BuildTask.createFn</code> still provide the function-syntax if you need it.</em></p> <h3 id="dependencies">Dependencies</h3> <p>The biggest change is the dependency syntax, <code class="language-plaintext highlighter-rouge">Target</code> rely on operators chaining (or methods doing the same) and the result looks good for linear dependencies:</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s2">"Clean"</span> <span class="o">==></span> <span class="s2">"BuildApp"</span> <span class="o">==></span> <span class="s2">"BuildTests"</span> <span class="o">==></span> <span class="s2">"Test"</span> <span class="o">==></span> <span class="s2">"PackageApp"</span> <span class="o">==></span> <span class="s2">"Default"</span> <span class="o">==></span> <span class="s2">"CI"</span> </code></pre></div></div> <p>But the dependencies here are simplified, they don’t reflect the reality of target dependencies. Clean shouldn’t be mandatory except on CI, packaging the app shouldn’t mandate running the tests and building the tests shouldn’t require building the app.</p> <p>The real dependencies are more like:</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s2">"Clean"</span> <span class="o">=?></span> <span class="s2">"BuildApp"</span> <span class="s2">"Clean"</span> <span class="o">=?></span> <span class="s2">"BuildTests"</span> <span class="s2">"Clean"</span> <span class="o">==></span> <span class="s2">"CI"</span> <span class="s2">"BuildApp"</span> <span class="o">==></span> <span class="s2">"PackageApp"</span> <span class="s2">"BuildTests"</span> <span class="o">==></span> <span class="s2">"Test"</span> <span class="s2">"PackageApp"</span> <span class="o">==></span> <span class="s2">"Default"</span> <span class="s2">"Test"</span> <span class="o">==></span> <span class="s2">"Default"</span> <span class="s2">"Default"</span> <span class="o">==></span> <span class="s2">"CI"</span> </code></pre></div></div> <p>And while the linear version was pretty readable, it isn’t the case here.</p> <p>The reason is that we are defining a directed acyclic graph by listing its edges, but there is another text representation of a DAG that is much more readable: listing all preceding vertex for each vertex.</p> <p>It’s a syntax that is already used by tools similar to FAKE like <a href="https://gulpjs.com/">Gulp</a> :</p> <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">gulp</span><span class="p">.</span><span class="nx">task</span><span class="p">(</span><span class="dl">"</span><span class="s2">css</span><span class="dl">"</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="cm">/* ...*/</span> <span class="p">});</span> <span class="nx">gulp</span><span class="p">.</span><span class="nx">task</span><span class="p">(</span><span class="dl">"</span><span class="s2">js</span><span class="dl">"</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="cm">/* ...*/</span> <span class="p">});</span> <span class="nx">gulp</span><span class="p">.</span><span class="nx">task</span><span class="p">(</span><span class="dl">"</span><span class="s2">test</span><span class="dl">"</span><span class="p">,</span> <span class="p">[</span><span class="dl">"</span><span class="s2">js</span><span class="dl">"</span><span class="p">],</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="cm">/* ...*/</span> <span class="p">});</span> <span class="nx">gulp</span><span class="p">.</span><span class="nx">task</span><span class="p">(</span><span class="dl">"</span><span class="s2">default</span><span class="dl">"</span><span class="p">,</span> <span class="p">[</span><span class="dl">"</span><span class="s2">css</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">test</span><span class="dl">"</span><span class="p">],</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="cm">/* ...*/</span> <span class="p">});</span> </code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">BuildTask</code> uses a similar syntax:</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">clean</span> <span class="p">=</span> <span class="nn">BuildTask</span><span class="p">.</span><span class="n">create</span> <span class="s2">"Clean"</span> <span class="bp">[]</span> <span class="p">{</span> <span class="c">(* ... *)</span> <span class="p">}</span> <span class="k">let</span> <span class="n">buildApp</span> <span class="p">=</span> <span class="nn">BuildTask</span><span class="p">.</span><span class="n">create</span> <span class="s2">"BuildApp"</span> <span class="p">[</span><span class="n">clean</span><span class="p">.</span><span class="nc">IfNeeded</span><span class="p">]</span> <span class="p">{</span> <span class="c">(* ... *)</span> <span class="p">}</span> <span class="k">let</span> <span class="n">packageApp</span> <span class="p">=</span> <span class="nn">BuildTask</span><span class="p">.</span><span class="n">create</span> <span class="s2">"PackageApp"</span> <span class="p">[</span><span class="n">buildApp</span><span class="p">]</span> <span class="p">{</span> <span class="c">(* ... *)</span> <span class="p">}</span> <span class="k">let</span> <span class="n">buildTests</span> <span class="p">=</span> <span class="nn">BuildTask</span><span class="p">.</span><span class="n">create</span> <span class="s2">"BuildTests"</span> <span class="p">[</span><span class="n">clean</span><span class="p">.</span><span class="nc">IfNeeded</span><span class="p">]</span> <span class="p">{</span> <span class="c">(* ... *)</span> <span class="p">}</span> <span class="k">let</span> <span class="n">test</span> <span class="p">=</span> <span class="nn">BuildTask</span><span class="p">.</span><span class="n">create</span> <span class="s2">"Test"</span> <span class="p">[</span><span class="n">buildTests</span><span class="p">]</span> <span class="p">{</span> <span class="c">(* ... *)</span> <span class="p">}</span> <span class="k">let</span> <span class="n">defaultTask</span> <span class="p">=</span> <span class="nn">BuildTask</span><span class="p">.</span><span class="n">createEmpty</span> <span class="s2">"Default"</span> <span class="p">[</span><span class="n">packageApp</span><span class="p">;</span> <span class="n">test</span><span class="p">]</span> <span class="k">let</span> <span class="n">ci</span> <span class="p">=</span> <span class="nn">BuildTask</span><span class="p">.</span><span class="n">createEmpty</span> <span class="s2">"CI"</span> <span class="p">[</span><span class="n">clean</span><span class="p">;</span> <span class="n">defaultTask</span><span class="p">]</span> </code></pre></div></div> <h3 id="ordering">Ordering</h3> <p>The direct consequence of the strongly-typed syntax along with the dependency syntax is that <code class="language-plaintext highlighter-rouge">BuildTask</code> enforces a strict ordering of the build script. Exactly as F# requires for functions inside a module.</p> <p>While I feared this consequence at first, after converting quite a lot of FAKE scripts it’s easy to adopt and make the build script order more logical for developers familiar with F#.</p> <h2 id="getting-started-samples">Getting started samples</h2> <p>Here are a few of the samples on the FAKE getting started page ported to <code class="language-plaintext highlighter-rouge">BuildTask</code>:</p> <p><a href="https://fake.build/fake-gettingstarted.html#Getting-started">GETTING STARTED</a>:</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">#</span><span class="n">r</span> <span class="s2">"paket: nuget BlackFox.Fake.BuildTask nuget Fake.Core.Target //"</span> <span class="p">#</span><span class="n">load</span> <span class="s2">"./.fake/build.fsx/intellisense.fsx"</span> <span class="k">open</span> <span class="nn">BlackFox</span><span class="p">.</span><span class="nc">Fake</span> <span class="k">open</span> <span class="nn">Fake</span><span class="p">.</span><span class="nc">Core</span> <span class="c1">// Default target</span> <span class="k">let</span> <span class="n">defaultTask</span> <span class="p">=</span> <span class="nn">BuildTask</span><span class="p">.</span><span class="n">create</span> <span class="s2">"Default"</span> <span class="bp">[]</span> <span class="p">{</span> <span class="nn">Trace</span><span class="p">.</span><span class="n">trace</span> <span class="s2">"Hello World from FAKE"</span> <span class="p">}</span> <span class="c1">// start build</span> <span class="nn">BuildTask</span><span class="p">.</span><span class="n">runOrDefault</span> <span class="n">defaultTask</span> </code></pre></div></div> <p><a href="https://fake.build/fake-gettingstarted.html#Cleaning-the-last-build-output">CLEANING THE LAST BUILD OUTPUT</a>:</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">#</span><span class="n">r</span> <span class="s2">"paket: nuget BlackFox.Fake.BuildTask nuget Fake.IO.FileSystem nuget Fake.Core.Target //"</span> <span class="p">#</span><span class="n">load</span> <span class="s2">"./.fake/build.fsx/intellisense.fsx"</span> <span class="k">open</span> <span class="nn">BlackFox</span><span class="p">.</span><span class="nc">Fake</span> <span class="k">open</span> <span class="nn">Fake</span><span class="p">.</span><span class="nc">Core</span> <span class="k">open</span> <span class="nn">Fake</span><span class="p">.</span><span class="nc">IO</span> <span class="c1">// Properties</span> <span class="k">let</span> <span class="n">buildDir</span> <span class="p">=</span> <span class="s2">"./build/"</span> <span class="c1">// Targets</span> <span class="k">let</span> <span class="n">cleanTask</span> <span class="p">=</span> <span class="nn">BuildTask</span><span class="p">.</span><span class="n">create</span> <span class="s2">"Clean"</span> <span class="bp">[]</span> <span class="p">{</span> <span class="nn">Shell</span><span class="p">.</span><span class="nc">CleanDir</span> <span class="n">buildDir</span> <span class="p">}</span> <span class="k">let</span> <span class="n">defaultTask</span> <span class="p">=</span> <span class="nn">BuildTask</span><span class="p">.</span><span class="n">create</span> <span class="s2">"Default"</span> <span class="p">[</span><span class="n">cleanTask</span><span class="p">]</span> <span class="p">{</span> <span class="nn">Trace</span><span class="p">.</span><span class="n">trace</span> <span class="s2">"Hello World from FAKE"</span> <span class="p">}</span> <span class="c1">// start build</span> <span class="nn">BuildTask</span><span class="p">.</span><span class="n">runOrDefault</span> <span class="n">defaultTask</span> </code></pre></div></div> <h2 id="conclusion">Conclusion</h2> <p>I invite any FAKE user whenever they like the current syntax or not to try it (<a href="https://www.nuget.org/packages/BlackFox.Fake.BuildTask/">NuGet</a>) and tell me what they think on <a href="https://twitter.com/virtualblackfox">twitter</a> or in a <a href="https://github.com/vbfox/FoxSharp/tree/master/src/BlackFox.Fake.BuildTask">GitHub</a> issue.</p> </article> </li> <li class="post-separator" /> <li class="post"> <div class="post-header"> <h2 class="post-title"> <a class="post-link" href="/2018/08/18/introducing-stidgen.html">Introducing Stidgen, an ID type generator</a> </h2> <span class="post-meta">Aug 18, 2018</span> </div> <article class="post-content"> <p><a href="https://github.com/vbfox/stidgen">Stidgen</a> is an old (4 years) project of mine that was created to fill a professional need but that I never really advertised until now. The name stands for “Strongly Typed ID types GENerator”.</p> <p>What it does is to generate C# types to wrap simple types used as identifiers (<code class="language-plaintext highlighter-rouge">string</code> -> <code class="language-plaintext highlighter-rouge">UserId</code>, <code class="language-plaintext highlighter-rouge">Guid</code> -> <code class="language-plaintext highlighter-rouge">TweetId</code>, …) to have nicely typed code.</p> <p>A simple <code class="language-plaintext highlighter-rouge">.stidgen</code> file (The format is documented on the <a href="https://github.com/vbfox/stidgen/blob/master/Readme.md">readme</a>) with the following content :</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="n">UserId</span><span class="p"><</span><span class="kt">string</span><span class="p">></span> <span class="k">public</span> <span class="n">CompanyId</span><span class="p"><</span><span class="kt">string</span><span class="p">></span> <span class="k">public</span> <span class="n">UploadedFileId</span><span class="p"><</span><span class="n">Guid</span><span class="p">></span> </code></pre></div></div> <p>Generate <a href="https://github.com/vbfox/stidgen/blob/1.1.0/samples/Simple.Generated.cs">a 837 lines file</a>, the types can then be used to get readable type signatures :</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">interface</span> <span class="nc">IPopulationQueries</span> <span class="p">{</span> <span class="n">UserInfo</span> <span class="nf">GetUserInfo</span><span class="p">(</span><span class="n">UserId</span> <span class="n">id</span><span class="p">);</span> <span class="n">CompanyId</span> <span class="nf">GetUserCompany</span><span class="p">(</span><span class="n">UserId</span> <span class="n">id</span><span class="p">);</span> <span class="n">IReadOnlyCollection</span><span class="p"><</span><span class="n">UserId</span><span class="p">></span> <span class="nf">GetCompanyUsers</span><span class="p">(</span><span class="n">CompanyId</span> <span class="n">id</span><span class="p">);</span> <span class="p">}</span> </code></pre></div></div> <h2 id="usage">Usage</h2> <p>The latest version of stidgen is available as a dotnet code (2.1+) tool. It can be installed globally like that :</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dotnet tool <span class="nb">install</span> <span class="nt">--global</span> stidgen </code></pre></div></div> <p>and can the be used from anywhere :</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>stidgen samples/Simple.stidgen </code></pre></div></div> <p>As a dotnet tool it can also be installed in a project and used from CI/FAKE. (No official FAKE module yet, it’ll come)</p> <h2 id="what-id-types-provide">What ID types provide</h2> <p>A very simple wrapper type can be generated by hand in a dozen of lines but stidgen provide a lot more features by default :</p> <ul> <li>An immutable structure with a single member, something that the JIT can recognize and optimize away.</li> <li>Casts from/to the underlying types.</li> <li>Equality members, interfaces and operators.</li> <li>Interning of string when it’s the underlying type, with nice optimizations to Equal and GetHashCode. I’ll try to blog about them it’s quite an interesting optimization.</li> <li><code class="language-plaintext highlighter-rouge">ToString()</code> and formattable overloads are lifted.</li> <li><code class="language-plaintext highlighter-rouge">DebuggerDisplay</code> is present to simplify debugging.</li> <li>Optional protobuf-net attributes generation.</li> </ul> <h2 id="comparison-with-f-solution">Comparison with F# solution</h2> <p>While stidgen is written in F# it doesn’t provide a way to generate ID types in F#. One of the reason is that generating formatted F# code is quite hard, but the other is that there is already 80% of the features directly available in the language.</p> <p>The solution to the same problem in F# is to use a single-case discriminated union:</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[<</span><span class="nc">Struct</span><span class="p">>]</span> <span class="k">type</span> <span class="nc">UserId</span> <span class="p">=</span> <span class="p">|</span> <span class="nc">UserId</span> <span class="k">of</span> <span class="kt">string</span> <span class="p">[<</span><span class="nc">Struct</span><span class="p">>]</span> <span class="k">type</span> <span class="nc">CompanyId</span> <span class="p">=</span> <span class="p">|</span> <span class="nc">CompanyId</span> <span class="k">of</span> <span class="kt">string</span> <span class="p">[<</span><span class="nc">Struct</span><span class="p">>]</span> <span class="k">type</span> <span class="nc">UploadedFileId</span> <span class="p">=</span> <span class="p">|</span> <span class="nc">UploadedFileId</span> <span class="k">of</span> <span class="nc">Guid</span> </code></pre></div></div> <p>And F# nicely provide most of what stidgen does by default (Equality interfaces and operators, immutability) but there are still a few missing things so maybe one day i’ll work on a F# generator :</p> <ul> <li>No way to automatically intern strings</li> <li>No interning-specific optimizations.</li> <li>No check for null (Producing nulls in F# is rare but .NET corelib/libraries can produce them)</li> <li><code class="language-plaintext highlighter-rouge">ToString</code> isn’t handled</li> <li>No <code class="language-plaintext highlighter-rouge">DebuggerDisplay</code></li> </ul> <p><em>Note: Stidgen generated types works very nicely from F#</em></p> <h2 id="the-code">The code</h2> <p>Stidgen is an F# project that uses <a href="https://github.com/dotnet/roslyn">Roslyn</a> the C# compiler platform to generate nicely formatted code automatically.</p> <p>The <a href="https://github.com/vbfox/stidgen/blob/1.1.0/src/BlackFox.Stidgen/FluentRoslyn.fs"><code class="language-plaintext highlighter-rouge">FluentRoslyn.fs</code></a> file in the respository contains an F# wrapper for Roslyn code generation that produce code like that :</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">if'</span> <span class="p">(</span><span class="n">equals</span> <span class="n">info</span><span class="p">.</span><span class="nc">ThisValueMemberAccess</span> <span class="nn">Literal</span><span class="p">.</span><span class="nc">Null</span><span class="p">)</span> <span class="p">(</span><span class="n">block</span> <span class="p">[|</span> <span class="n">ret</span> <span class="p">(</span><span class="n">invocation</span> <span class="p">(</span><span class="n">dottedMemberAccess'</span> <span class="p">[</span><span class="s2">"System"</span><span class="p">;</span> <span class="s2">"Convert"</span><span class="p">;</span> <span class="n">m</span><span class="p">.</span><span class="nc">Name</span><span class="o">])</span> <span class="p">[|</span> <span class="nn">Literal</span><span class="p">.</span><span class="nc">Null</span> <span class="o">|])</span> <span class="o">|])</span> </code></pre></div></div> <h2 id="conclusion">Conclusion</h2> <p>Stidgen is a project that we use extensively at work but I feel that we might not be the only ones that want strongly typed code in a C# codebase. So if you have this need or a similar one, why not try it ?</p> </article> </li> <li class="post-separator" /> <li class="post"> <div class="post-header"> <h2 class="post-title"> <a class="post-link" href="/2018/02/08/fable-react-2-optimizing-react.html">Fable: React you can be proud of ! Part 2: Optimizing React</a> </h2> <span class="post-meta">Feb 8, 2018</span> </div> <article class="post-content"> <p>To optimize React we’ll need to dig into how it works and I’ll show a series of specific optimizations to use.</p> <p>The post is part of a series on Fable / React optimizations split in 3 parts:</p> <ol> <li><a href="/2018/02/06/fable-react-1-react-in-fable-land.html">React in Fable land</a></li> <li><a href="/2018/02/08/fable-react-2-optimizing-react.html">Optimizing React</a> (This one)</li> <li>Applying to Elmish</li> </ol> <h2 id="how-does-react-work">How does React work</h2> <p>The mechanism is described in a lot of details on the <a href="https://reactjs.org/docs/reconciliation.html">Reconciliation</a> page of React documentation, I won’t repeat the details but essentially React Keep a representation of what it’s element tree is currently, and each time a change need to propagate it will evaluate what the element tree should now be and apply the diff.</p> <p>A few rules are important for performance:</p> <ul> <li>Change of element type or component type makes React abandon any DOM diffing and the old tree is destroyed in favor of the new one. <ul> <li>This should be a rare occurrence, any big DOM change is pretty slow as it will involve a lot of destruction / creation by the browser and a lot of reflowing.</li> <li>On the other hand we want this to happen if we know that the HTML elements under some Component will drastically change: no need to force React to diff 100s of elements if we know that it’s a very different page that is shown. The diff also has a price.</li> </ul> </li> <li>React mostly compares elements in order so adding an element at the start of a parent will change ALL children. The <code class="language-plaintext highlighter-rouge">key</code> attribute should be used to override this behaviour for anything that is more or less a list of elements. <ul> <li>Keys should also be stable, an array index for example is a pretty bad key as it’s nearly what React does when there are no keys.</li> </ul> </li> <li>The fastest way to render a component is to not render it at all, so <code class="language-plaintext highlighter-rouge">shouldComponentUpdate</code> (And <code class="language-plaintext highlighter-rouge">PureComponent</code> that is using it) are the best tools for us.</li> <li>Functional components are always rendered, but they are cheaper than normal ones as all the lifetime methods are bypassed too.</li> </ul> <p>We can roughly consider for optimization purpose that each component in the tree is in one of these 4 states after each change (ordered from better performance-wise to worse) :</p> <ol> <li>Not considered (Because its parent wasn’t re-rendered) ❄️❄️</li> <li>Returned false to <code class="language-plaintext highlighter-rouge">shouldComponentUpdate</code> ❄️</li> <li>Render was called but returned the same tree as before 🔥</li> <li>Render was called but the tree is different and the document DOM need to be mutated 🔥🔥</li> </ol> <h2 id="purecomponent">PureComponent</h2> <p>The first optimization that is especially useful for us in F# is the <code class="language-plaintext highlighter-rouge">PureComponent</code>. It’s a component that only updates when one of the elements in its props or state changed and the comparison is done in a shallow way (by comparing references).</p> <p>It’s ideal when everything you manipulate is immutable, you know, like F# records 😉.</p> <p>Let’s take a small sample to see how it’s good for us. Test it as-is and with the <code class="language-plaintext highlighter-rouge">Component</code> replaced with a <code class="language-plaintext highlighter-rouge">PureComponent</code> :</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="nc">Canary</span><span class="p">(</span><span class="n">initialProps</span><span class="p">)</span> <span class="p">=</span> <span class="k">inherit</span> <span class="nc">Component</span><span class="p"><</span><span class="n">obj</span><span class="p">,</span> <span class="n">obj</span><span class="o">>(</span><span class="n">initialProps</span><span class="p">)</span> <span class="c1">// <-- Change to PureComponent here</span> <span class="k">let</span> <span class="k">mutable</span> <span class="n">x</span> <span class="p">=</span> <span class="mi">0</span> <span class="k">override</span> <span class="n">this</span><span class="p">.</span><span class="n">render</span><span class="bp">()</span> <span class="p">=</span> <span class="n">x</span> <span class="p"><-</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">1</span> <span class="n">div</span> <span class="bp">[]</span> <span class="p">[</span><span class="n">ofInt</span> <span class="n">x</span><span class="p">;</span> <span class="n">str</span> <span class="s2">" = "</span><span class="p">;</span> <span class="n">str</span> <span class="p">(</span><span class="k">if</span> <span class="n">x</span> <span class="p">></span> <span class="mi">1</span> <span class="k">then</span> <span class="s2">"☠️"</span> <span class="k">else</span> <span class="s2">"🐤️"</span><span class="p">)</span> <span class="p">]</span> <span class="k">let</span> <span class="k">inline</span> <span class="n">canary</span> <span class="bp">()</span> <span class="p">=</span> <span class="n">ofType</span><span class="p"><</span><span class="nc">Canary</span><span class="o">,_,_></span> <span class="n">createEmpty</span> <span class="bp">[]</span> <span class="k">type</span> <span class="nc">CounterState</span> <span class="p">=</span> <span class="p">{</span> <span class="n">counter</span><span class="p">:</span> <span class="kt">int</span> <span class="p">}</span> <span class="k">type</span> <span class="nc">Counter</span><span class="p">(</span><span class="n">initialProps</span><span class="p">)</span> <span class="k">as</span> <span class="n">this</span> <span class="p">=</span> <span class="k">inherit</span> <span class="nc">Component</span><span class="p"><</span><span class="n">obj</span><span class="p">,</span> <span class="nc">CounterState</span><span class="o">>(</span><span class="n">initialProps</span><span class="p">)</span> <span class="k">do</span> <span class="n">this</span><span class="p">.</span><span class="n">setInitState</span><span class="o">({</span> <span class="n">counter</span> <span class="p">=</span> <span class="mi">0</span><span class="o">})</span> <span class="k">let</span> <span class="n">add</span> <span class="p">=</span> <span class="n">this</span><span class="p">.</span><span class="nc">Add</span> <span class="k">member</span> <span class="n">this</span><span class="p">.</span><span class="nc">Add</span><span class="o">(_:</span><span class="nc">MouseEvent</span><span class="p">)</span> <span class="p">=</span> <span class="n">this</span><span class="p">.</span><span class="n">setState</span><span class="o">({</span> <span class="n">counter</span> <span class="p">=</span> <span class="n">this</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">counter</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">})</span> <span class="k">override</span> <span class="n">this</span><span class="p">.</span><span class="n">render</span><span class="bp">()</span> <span class="p">=</span> <span class="n">div</span> <span class="bp">[]</span> <span class="p">[</span> <span class="n">canary</span> <span class="bp">()</span> <span class="n">div</span> <span class="bp">[]</span> <span class="p">[</span> <span class="n">str</span> <span class="s2">"Counter = "</span><span class="p">;</span> <span class="n">ofInt</span> <span class="n">this</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">counter</span> <span class="p">]</span> <span class="n">button</span> <span class="p">[</span><span class="nc">OnClick</span> <span class="n">add</span><span class="p">]</span> <span class="p">[</span><span class="n">str</span> <span class="s2">"👍"</span><span class="p">]</span> <span class="p">]</span> <span class="k">let</span> <span class="k">inline</span> <span class="n">counter</span> <span class="bp">()</span> <span class="p">=</span> <span class="n">ofType</span><span class="p"><</span><span class="nc">Counter</span><span class="o">,_,_></span> <span class="n">createEmpty</span> <span class="bp">[]</span> <span class="k">let</span> <span class="n">init</span><span class="bp">()</span> <span class="p">=</span> <span class="nn">ReactDom</span><span class="p">.</span><span class="n">render</span><span class="p">(</span><span class="n">counter</span> <span class="bp">()</span><span class="p">,</span> <span class="n">document</span><span class="p">.</span><span class="n">getElementById</span><span class="p">(</span><span class="s2">"root"</span><span class="o">))</span> </code></pre></div></div> <p>Before: <img src="/assets/fable-react/pure-component-dead.gif" alt="Dead Canary" /></p> <p>After: <img src="/assets/fable-react/pure-component-alive.gif" alt="Living Canary" /></p> <p>While our canary has no reason to update, each time the button is clicked it will actually re-render. But as soon as we convert it to a <code class="language-plaintext highlighter-rouge">PureComponent</code> it’s not updating anymore: none of its props or state change so react doesn’t even call <code class="language-plaintext highlighter-rouge">render()</code>.</p> <h2 id="beware-passing-functions">Beware: Passing functions</h2> <p>If you look in the previous samples, each time I pass a function it’s never a lambda declared directly in <code class="language-plaintext highlighter-rouge">render()</code> or even a member reference but it’s a field that points to a member.</p> <p>The reason for that is that for react to not apply changes for DOM elements or for <code class="language-plaintext highlighter-rouge">PureComponent</code> the references must be the same and lambdas are re-recreated each time so their reference would be different.</p> <p>But members? Members stay the same so we should be able to pass <code class="language-plaintext highlighter-rouge">this.Add</code> and have it work. But JavaScript is a weird language where passing <code class="language-plaintext highlighter-rouge">this.Add</code> would pass the method add without any <code class="language-plaintext highlighter-rouge">this</code> attached, so to keep the semantic of the F# language Fable helpfully do it for us and transpiles it to <code class="language-plaintext highlighter-rouge">this.Add.bind(this)</code> instead. But this also re-creates a reference each time so we must capture the bound version in a variable during the construction of the object.</p> <p>It’s hard to prove it with the <code class="language-plaintext highlighter-rouge">button</code> so let’s prove it by moving the button creation to our lovely 🐤:</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="nc">CanaryProps</span> <span class="p">=</span> <span class="p">{</span> <span class="n">add</span><span class="p">:</span> <span class="nc">MouseEvent</span> <span class="p">-></span> <span class="kt">unit</span> <span class="p">}</span> <span class="k">type</span> <span class="nc">Canary</span><span class="p">(</span><span class="n">initialProps</span><span class="p">)</span> <span class="p">=</span> <span class="k">inherit</span> <span class="nc">PureComponent</span><span class="p"><</span><span class="nc">CanaryProps</span><span class="p">,</span> <span class="n">obj</span><span class="o">>(</span><span class="n">initialProps</span><span class="p">)</span> <span class="k">let</span> <span class="k">mutable</span> <span class="n">x</span> <span class="p">=</span> <span class="mi">0</span> <span class="k">override</span> <span class="n">this</span><span class="p">.</span><span class="n">render</span><span class="bp">()</span> <span class="p">=</span> <span class="n">x</span> <span class="p"><-</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">1</span> <span class="n">div</span> <span class="bp">[]</span> <span class="p">[</span> <span class="n">button</span> <span class="p">[</span><span class="nc">OnClick</span> <span class="n">this</span><span class="p">.</span><span class="n">props</span><span class="p">.</span><span class="n">add</span><span class="p">]</span> <span class="p">[</span><span class="n">str</span> <span class="s2">"👍"</span><span class="p">]</span> <span class="n">span</span> <span class="bp">[]</span> <span class="p">[</span><span class="n">ofInt</span> <span class="n">x</span><span class="p">;</span> <span class="n">str</span> <span class="s2">" = "</span><span class="p">;</span> <span class="n">str</span> <span class="p">(</span><span class="k">if</span> <span class="n">x</span> <span class="p">></span> <span class="mi">1</span> <span class="k">then</span> <span class="s2">"☠️"</span> <span class="k">else</span> <span class="s2">"️️️🐤️"</span><span class="p">)</span> <span class="p">]</span> <span class="p">]</span> <span class="k">let</span> <span class="k">inline</span> <span class="n">canary</span> <span class="n">props</span> <span class="p">=</span> <span class="n">ofType</span><span class="p"><</span><span class="nc">Canary</span><span class="o">,_,_></span> <span class="n">props</span> <span class="bp">[]</span> <span class="k">type</span> <span class="nc">CounterState</span> <span class="p">=</span> <span class="p">{</span> <span class="n">counter</span><span class="p">:</span> <span class="kt">int</span> <span class="p">}</span> <span class="k">type</span> <span class="nc">Counter</span><span class="p">(</span><span class="n">initialProps</span><span class="p">)</span> <span class="k">as</span> <span class="n">this</span> <span class="p">=</span> <span class="k">inherit</span> <span class="nc">Component</span><span class="p"><</span><span class="n">obj</span><span class="p">,</span> <span class="nc">CounterState</span><span class="o">>(</span><span class="n">initialProps</span><span class="p">)</span> <span class="k">do</span> <span class="n">this</span><span class="p">.</span><span class="n">setInitState</span><span class="o">({</span> <span class="n">counter</span> <span class="p">=</span> <span class="mi">0</span><span class="o">})</span> <span class="k">let</span> <span class="n">add</span> <span class="p">=</span> <span class="n">this</span><span class="p">.</span><span class="nc">Add</span> <span class="k">member</span> <span class="n">this</span><span class="p">.</span><span class="nc">Add</span><span class="o">(_:</span><span class="nc">MouseEvent</span><span class="p">)</span> <span class="p">=</span> <span class="n">this</span><span class="p">.</span><span class="n">setState</span><span class="o">({</span> <span class="n">counter</span> <span class="p">=</span> <span class="n">this</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">counter</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">})</span> <span class="k">override</span> <span class="n">this</span><span class="p">.</span><span class="n">render</span><span class="bp">()</span> <span class="p">=</span> <span class="n">div</span> <span class="bp">[]</span> <span class="p">[</span> <span class="n">canary</span> <span class="p">{</span> <span class="n">add</span> <span class="p">=</span> <span class="n">add</span> <span class="p">}</span> <span class="n">canary</span> <span class="p">{</span> <span class="n">add</span> <span class="p">=</span> <span class="n">this</span><span class="p">.</span><span class="nc">Add</span> <span class="p">}</span> <span class="n">canary</span> <span class="p">{</span> <span class="n">add</span> <span class="p">=</span> <span class="p">(</span><span class="k">fun</span> <span class="p">_</span> <span class="p">-></span> <span class="n">this</span><span class="p">.</span><span class="n">setState</span><span class="o">({</span> <span class="n">counter</span> <span class="p">=</span> <span class="n">this</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">counter</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">}))</span> <span class="p">}</span> <span class="n">div</span> <span class="bp">[]</span> <span class="p">[</span> <span class="n">str</span> <span class="s2">"Counter = "</span><span class="p">;</span> <span class="n">ofInt</span> <span class="n">this</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">counter</span> <span class="p">]</span> <span class="p">]</span> <span class="k">let</span> <span class="k">inline</span> <span class="n">counter</span> <span class="bp">()</span> <span class="p">=</span> <span class="n">ofType</span><span class="p"><</span><span class="nc">Counter</span><span class="o">,_,_></span> <span class="n">createEmpty</span> <span class="bp">[]</span> <span class="k">let</span> <span class="n">init</span><span class="bp">()</span> <span class="p">=</span> <span class="nn">ReactDom</span><span class="p">.</span><span class="n">render</span><span class="p">(</span><span class="n">counter</span> <span class="bp">()</span><span class="p">,</span> <span class="n">document</span><span class="p">.</span><span class="n">getElementById</span><span class="p">(</span><span class="s2">"root"</span><span class="o">))</span> </code></pre></div></div> <p><img src="/assets/fable-react/passing-functions.gif" alt="Only the first canary live" /></p> <h2 id="using-toarraytolist-and-refs">Using <code class="language-plaintext highlighter-rouge">toArray</code>/<code class="language-plaintext highlighter-rouge">toList</code> and refs</h2> <p>It’s tempting in F# to use list expressions to build React children even when we have lists as it allows for a very nice syntax, but it can be a performance problem and force useless renders. Let’s see a problematic sample:</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="nc">CanaryProps</span> <span class="p">=</span> <span class="p">{</span> <span class="n">name</span><span class="p">:</span> <span class="kt">string</span> <span class="p">}</span> <span class="k">type</span> <span class="nc">Canary</span><span class="p">(</span><span class="n">initialProps</span><span class="p">)</span> <span class="p">=</span> <span class="k">inherit</span> <span class="nc">PureComponent</span><span class="p"><</span><span class="nc">CanaryProps</span><span class="p">,</span> <span class="n">obj</span><span class="o">>(</span><span class="n">initialProps</span><span class="p">)</span> <span class="k">let</span> <span class="k">mutable</span> <span class="n">x</span> <span class="p">=</span> <span class="mi">0</span> <span class="k">override</span> <span class="n">this</span><span class="p">.</span><span class="n">render</span><span class="bp">()</span> <span class="p">=</span> <span class="n">x</span> <span class="p"><-</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">1</span> <span class="n">div</span> <span class="bp">[]</span> <span class="p">[</span><span class="n">str</span> <span class="p">(</span><span class="k">if</span> <span class="n">x</span> <span class="p">></span> <span class="mi">1</span> <span class="k">then</span> <span class="s2">"☠️"</span> <span class="k">else</span> <span class="s2">"️️️🐤️"</span><span class="o">);</span> <span class="n">str</span> <span class="s2">" "</span><span class="p">;</span> <span class="n">str</span> <span class="n">this</span><span class="p">.</span><span class="n">props</span><span class="p">.</span><span class="n">name</span> <span class="p">]</span> <span class="k">let</span> <span class="k">inline</span> <span class="n">canary</span> <span class="n">props</span> <span class="p">=</span> <span class="n">ofType</span><span class="p"><</span><span class="nc">Canary</span><span class="o">,_,_></span> <span class="n">props</span> <span class="bp">[]</span> <span class="k">let</span> <span class="n">goodNames</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"Chantilly"</span><span class="p">;</span> <span class="s2">"Pepe"</span><span class="p">;</span> <span class="s2">"Lester"</span><span class="p">;</span> <span class="s2">"Pete"</span><span class="p">;</span> <span class="s2">"Baby"</span><span class="p">;</span> <span class="s2">"Sunny"</span><span class="p">;</span> <span class="s2">"Bluebird"</span><span class="p">]</span> <span class="k">type</span> <span class="nc">CanariesState</span> <span class="p">=</span> <span class="p">{</span> <span class="n">i</span><span class="p">:</span> <span class="kt">int</span><span class="p">;</span> <span class="n">names</span><span class="p">:</span> <span class="kt">string</span> <span class="kt">list</span> <span class="p">}</span> <span class="k">type</span> <span class="nc">Counter</span><span class="p">(</span><span class="n">initialProps</span><span class="p">)</span> <span class="k">as</span> <span class="n">this</span> <span class="p">=</span> <span class="k">inherit</span> <span class="nc">Component</span><span class="p"><</span><span class="n">obj</span><span class="p">,</span> <span class="nc">CanariesState</span><span class="o">>(</span><span class="n">initialProps</span><span class="p">)</span> <span class="k">do</span> <span class="n">this</span><span class="p">.</span><span class="n">setInitState</span><span class="o">({</span> <span class="n">i</span> <span class="p">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">names</span> <span class="p">=</span> <span class="bp">[]</span> <span class="o">})</span> <span class="k">let</span> <span class="n">add</span> <span class="p">=</span> <span class="n">this</span><span class="p">.</span><span class="nc">Add</span> <span class="k">member</span> <span class="n">this</span><span class="p">.</span><span class="nc">Add</span><span class="o">(_:</span><span class="nc">MouseEvent</span><span class="p">)</span> <span class="p">=</span> <span class="k">let</span> <span class="n">name</span> <span class="p">=</span> <span class="n">goodNames</span><span class="o">.[</span><span class="n">this</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">i</span> <span class="o">%</span> <span class="n">goodNames</span><span class="p">.</span><span class="nc">Length</span><span class="p">]</span> <span class="k">let</span> <span class="n">names</span> <span class="p">=</span> <span class="n">name</span> <span class="p">::</span> <span class="n">this</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">names</span> <span class="n">this</span><span class="p">.</span><span class="n">setState</span><span class="o">({</span> <span class="n">i</span> <span class="p">=</span> <span class="n">this</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="n">names</span> <span class="p">=</span> <span class="n">names</span> <span class="o">})</span> <span class="k">override</span> <span class="n">this</span><span class="p">.</span><span class="n">render</span><span class="bp">()</span> <span class="p">=</span> <span class="n">div</span> <span class="bp">[]</span> <span class="p">[</span> <span class="k">yield</span> <span class="n">button</span> <span class="p">[</span><span class="nc">OnClick</span> <span class="n">add</span><span class="p">]</span> <span class="p">[</span><span class="n">str</span> <span class="s2">"🥚"</span><span class="p">]</span> <span class="k">yield</span><span class="o">!</span> <span class="n">this</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">names</span> <span class="p">|></span> <span class="nn">List</span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="k">fun</span> <span class="n">n</span> <span class="p">-></span> <span class="n">canary</span> <span class="p">{</span> <span class="n">name</span> <span class="p">=</span> <span class="n">n</span> <span class="o">})</span> <span class="p">]</span> <span class="k">let</span> <span class="k">inline</span> <span class="n">counter</span> <span class="bp">()</span> <span class="p">=</span> <span class="n">ofType</span><span class="p"><</span><span class="nc">Counter</span><span class="o">,_,_></span> <span class="n">createEmpty</span> <span class="bp">[]</span> <span class="k">let</span> <span class="n">init</span><span class="bp">()</span> <span class="p">=</span> <span class="nn">ReactDom</span><span class="p">.</span><span class="n">render</span><span class="p">(</span><span class="n">counter</span> <span class="bp">()</span><span class="p">,</span> <span class="n">document</span><span class="p">.</span><span class="n">getElementById</span><span class="p">(</span><span class="s2">"root"</span><span class="o">))</span> </code></pre></div></div> <p><img src="/assets/fable-react/names-dead.gif" alt="Only the last canary live" /></p> <p>It seem that <em>Chantilly</em> survives but in fact it’s an illusion, a new element is always created at the end with his name, and all others are mutated.</p> <p>So let’s fix it by exposing an array via <code class="language-plaintext highlighter-rouge">toList</code> and assigning an unique key to all our canaries:</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="nc">CanaryProps</span> <span class="p">=</span> <span class="p">{</span> <span class="n">key</span><span class="p">:</span> <span class="kt">string</span><span class="p">;</span> <span class="n">name</span><span class="p">:</span> <span class="kt">string</span> <span class="p">}</span> <span class="k">type</span> <span class="nc">Canary</span><span class="p">(</span><span class="n">initialProps</span><span class="p">)</span> <span class="p">=</span> <span class="k">inherit</span> <span class="nc">PureComponent</span><span class="p"><</span><span class="nc">CanaryProps</span><span class="p">,</span> <span class="n">obj</span><span class="o">>(</span><span class="n">initialProps</span><span class="p">)</span> <span class="k">let</span> <span class="k">mutable</span> <span class="n">x</span> <span class="p">=</span> <span class="mi">0</span> <span class="k">override</span> <span class="n">this</span><span class="p">.</span><span class="n">render</span><span class="bp">()</span> <span class="p">=</span> <span class="n">x</span> <span class="p"><-</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">1</span> <span class="n">div</span> <span class="bp">[]</span> <span class="p">[</span><span class="n">str</span> <span class="p">(</span><span class="k">if</span> <span class="n">x</span> <span class="p">></span> <span class="mi">1</span> <span class="k">then</span> <span class="s2">"☠️"</span> <span class="k">else</span> <span class="s2">"️️️🐤️"</span><span class="o">);</span> <span class="n">str</span> <span class="s2">" "</span><span class="p">;</span> <span class="n">str</span> <span class="n">this</span><span class="p">.</span><span class="n">props</span><span class="p">.</span><span class="n">name</span> <span class="p">]</span> <span class="k">let</span> <span class="k">inline</span> <span class="n">canary</span> <span class="n">props</span> <span class="p">=</span> <span class="n">ofType</span><span class="p"><</span><span class="nc">Canary</span><span class="o">,_,_></span> <span class="n">props</span> <span class="bp">[]</span> <span class="k">let</span> <span class="n">goodNames</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"Chantilly"</span><span class="p">;</span> <span class="s2">"Pepe"</span><span class="p">;</span> <span class="s2">"Lester"</span><span class="p">;</span> <span class="s2">"Pete"</span><span class="p">;</span> <span class="s2">"Baby"</span><span class="p">;</span> <span class="s2">"Sunny"</span><span class="p">;</span> <span class="s2">"Bluebird"</span><span class="p">]</span> <span class="k">type</span> <span class="nc">CanariesState</span> <span class="p">=</span> <span class="p">{</span> <span class="n">i</span><span class="p">:</span> <span class="kt">int</span><span class="p">;</span> <span class="n">canaries</span><span class="p">:</span> <span class="p">(</span><span class="kt">int</span><span class="p">*</span><span class="kt">string</span><span class="p">)</span> <span class="kt">list</span> <span class="p">}</span> <span class="k">type</span> <span class="nc">Counter</span><span class="p">(</span><span class="n">initialProps</span><span class="p">)</span> <span class="k">as</span> <span class="n">this</span> <span class="p">=</span> <span class="k">inherit</span> <span class="nc">Component</span><span class="p"><</span><span class="n">obj</span><span class="p">,</span> <span class="nc">CanariesState</span><span class="o">>(</span><span class="n">initialProps</span><span class="p">)</span> <span class="k">do</span> <span class="n">this</span><span class="p">.</span><span class="n">setInitState</span><span class="o">({</span> <span class="n">i</span> <span class="p">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">canaries</span> <span class="p">=</span> <span class="bp">[]</span> <span class="o">})</span> <span class="k">let</span> <span class="n">add</span> <span class="p">=</span> <span class="n">this</span><span class="p">.</span><span class="nc">Add</span> <span class="k">member</span> <span class="n">this</span><span class="p">.</span><span class="nc">Add</span><span class="o">(_:</span><span class="nc">MouseEvent</span><span class="p">)</span> <span class="p">=</span> <span class="k">let</span> <span class="n">name</span> <span class="p">=</span> <span class="n">goodNames</span><span class="o">.[</span><span class="n">this</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">i</span> <span class="o">%</span> <span class="n">goodNames</span><span class="p">.</span><span class="nc">Length</span><span class="p">]</span> <span class="k">let</span> <span class="n">canaries</span> <span class="p">=</span> <span class="p">(</span><span class="n">this</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">i</span><span class="p">,</span><span class="n">name</span><span class="p">)</span> <span class="p">::</span> <span class="n">this</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">canaries</span> <span class="n">this</span><span class="p">.</span><span class="n">setState</span><span class="o">({</span> <span class="n">i</span> <span class="p">=</span> <span class="n">this</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="n">canaries</span> <span class="p">=</span> <span class="n">canaries</span> <span class="o">})</span> <span class="k">override</span> <span class="n">this</span><span class="p">.</span><span class="n">render</span><span class="bp">()</span> <span class="p">=</span> <span class="n">div</span> <span class="bp">[]</span> <span class="p">[</span> <span class="n">button</span> <span class="p">[</span><span class="nc">OnClick</span> <span class="n">add</span><span class="p">]</span> <span class="p">[</span><span class="n">str</span> <span class="s2">"🥚"</span><span class="p">]</span> <span class="n">this</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">canaries</span> <span class="p">|></span> <span class="nn">List</span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="k">fun</span> <span class="p">(</span><span class="n">i</span><span class="p">,</span><span class="n">n</span><span class="p">)</span> <span class="p">-></span> <span class="n">canary</span> <span class="p">{</span> <span class="n">key</span> <span class="p">=</span> <span class="n">i</span><span class="p">.</span><span class="nc">ToString</span><span class="bp">()</span><span class="p">;</span> <span class="n">name</span> <span class="p">=</span> <span class="n">n</span> <span class="o">})</span> <span class="p">|></span> <span class="n">ofList</span> <span class="p">]</span> <span class="k">let</span> <span class="k">inline</span> <span class="n">counter</span> <span class="bp">()</span> <span class="p">=</span> <span class="n">ofType</span><span class="p"><</span><span class="nc">Counter</span><span class="o">,_,_></span> <span class="n">createEmpty</span> <span class="bp">[]</span> <span class="k">let</span> <span class="n">init</span><span class="bp">()</span> <span class="p">=</span> <span class="nn">ReactDom</span><span class="p">.</span><span class="n">render</span><span class="p">(</span><span class="n">counter</span> <span class="bp">()</span><span class="p">,</span> <span class="n">document</span><span class="p">.</span><span class="n">getElementById</span><span class="p">(</span><span class="s2">"root"</span><span class="o">))</span> </code></pre></div></div> <p><img src="/assets/fable-react/names-ok.gif" alt="Every canary live" /></p> <p>We could have kept using <code class="language-plaintext highlighter-rouge">yield!</code> instead of using <code class="language-plaintext highlighter-rouge">ofList</code> and it would have worked here with only the keys but it’s better to always use <code class="language-plaintext highlighter-rouge">ofList</code>.</p> <p>By using it an array is passed to React and it will warn us on the console if we forget to use <code class="language-plaintext highlighter-rouge">key</code>.</p> <p>It also creates a new scope, avoiding problems if we wanted to show another list in the same parent with keys in common (duplicate keys under a same parent aren’t supposed to happen).</p> <h2 id="functional-components-in-other-modules">Functional components in other modules</h2> <p><strong>OBSOLETE: This problem doesn’t affect Fable 2</strong></p> <p>This is more of a current Fable issue/bug that might be solved at some point than something coming from React but it can wreck performance completely.</p> <p>The problem is that while directly referencing a function like <code class="language-plaintext highlighter-rouge">List.map foo</code> will generate something like <code class="language-plaintext highlighter-rouge">listMap(foo)</code>. Doing the same with a function that is exposed by a module <code class="language-plaintext highlighter-rouge">List.map m.foo</code> will generate <code class="language-plaintext highlighter-rouge">listMap(foo.m.bind(foo))</code>. It’s necessary when the target function is a javascript one as some of them require it but is useless for F# functions. Fable isn’t currently smart enough to differentiate them.</p> <p><em>Fable was actually doing it for all function creating the problem for all Functional components but I fixed it in a PR released as part of Fable 1.3.8</em></p> <p>This problem has exactly the same cause as the one evoked in <strong>Beware: Passing functions</strong> but it has a simple workaround: use the function via <code class="language-plaintext highlighter-rouge">ofFunction</code> in the same module in a <strong>non-inline</strong> function and expose that instead of the Functional Component itself.</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// The variable need to be extracted because what happens is worse</span> <span class="c1">// than just render() being called multiple time: The whole component</span> <span class="c1">// is re-created on each change !</span> <span class="k">let</span> <span class="k">mutable</span> <span class="n">x</span> <span class="p">=</span> <span class="mi">0</span> <span class="k">type</span> <span class="nc">Canary</span><span class="p">(</span><span class="n">initialProps</span><span class="p">)</span> <span class="p">=</span> <span class="k">inherit</span> <span class="nc">PureComponent</span><span class="p"><</span><span class="n">obj</span><span class="p">,</span> <span class="n">obj</span><span class="o">>(</span><span class="n">initialProps</span><span class="p">)</span> <span class="k">override</span> <span class="n">this</span><span class="p">.</span><span class="n">render</span><span class="bp">()</span> <span class="p">=</span> <span class="n">x</span> <span class="p"><-</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">1</span> <span class="n">div</span> <span class="bp">[]</span> <span class="p">[</span><span class="n">ofInt</span> <span class="n">x</span><span class="p">;</span> <span class="n">str</span> <span class="s2">" = "</span><span class="p">;</span> <span class="n">str</span> <span class="p">(</span><span class="k">if</span> <span class="n">x</span> <span class="p">></span> <span class="mi">1</span> <span class="k">then</span> <span class="s2">"☠️"</span> <span class="k">else</span> <span class="s2">"🐤️"</span><span class="p">)</span> <span class="p">]</span> <span class="k">let</span> <span class="k">inline</span> <span class="n">canary</span> <span class="bp">()</span> <span class="p">=</span> <span class="n">ofType</span><span class="p"><</span><span class="nc">Canary</span><span class="o">,_,_></span> <span class="n">createEmpty</span> <span class="bp">[]</span> <span class="k">module</span> <span class="nc">WrapperModule</span> <span class="p">=</span> <span class="k">let</span> <span class="nc">Wrapper</span><span class="p">(</span><span class="n">props</span><span class="p">:</span> <span class="n">obj</span><span class="p">)</span> <span class="p">=</span> <span class="n">div</span> <span class="bp">[]</span> <span class="p">[</span> <span class="n">h3</span> <span class="bp">[]</span> <span class="p">[</span><span class="n">str</span> <span class="s2">"In module"</span><span class="p">]</span> <span class="n">canary</span> <span class="bp">()</span> <span class="p">]</span> <span class="c1">// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</span> <span class="c1">// Remove 'inline' here and the problem is solved, MAGIC !</span> <span class="c1">// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</span> <span class="k">let</span> <span class="k">inline</span> <span class="n">wrapper</span> <span class="bp">()</span> <span class="p">=</span> <span class="n">ofFunction</span> <span class="nc">Wrapper</span> <span class="n">createEmpty</span> <span class="bp">[]</span> <span class="k">type</span> <span class="nc">CounterState</span> <span class="p">=</span> <span class="p">{</span> <span class="n">counter</span><span class="p">:</span> <span class="kt">int</span> <span class="p">}</span> <span class="k">type</span> <span class="nc">Counter</span><span class="p">(</span><span class="n">initialProps</span><span class="p">)</span> <span class="k">as</span> <span class="n">this</span> <span class="p">=</span> <span class="k">inherit</span> <span class="nc">Component</span><span class="p"><</span><span class="n">obj</span><span class="p">,</span> <span class="nc">CounterState</span><span class="o">>(</span><span class="n">initialProps</span><span class="p">)</span> <span class="k">do</span> <span class="n">this</span><span class="p">.</span><span class="n">setInitState</span><span class="o">({</span> <span class="n">counter</span> <span class="p">=</span> <span class="mi">0</span><span class="o">})</span> <span class="k">let</span> <span class="n">add</span> <span class="p">=</span> <span class="n">this</span><span class="p">.</span><span class="nc">Add</span> <span class="k">member</span> <span class="n">this</span><span class="p">.</span><span class="nc">Add</span><span class="o">(_:</span><span class="nc">MouseEvent</span><span class="p">)</span> <span class="p">=</span> <span class="n">this</span><span class="p">.</span><span class="n">setState</span><span class="o">({</span> <span class="n">counter</span> <span class="p">=</span> <span class="n">this</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">counter</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">})</span> <span class="k">override</span> <span class="n">this</span><span class="p">.</span><span class="n">render</span><span class="bp">()</span> <span class="p">=</span> <span class="n">div</span> <span class="bp">[]</span> <span class="p">[</span> <span class="nn">WrapperModule</span><span class="p">.</span><span class="n">wrapper</span><span class="bp">()</span> <span class="n">div</span> <span class="bp">[]</span> <span class="p">[</span> <span class="n">str</span> <span class="s2">"Counter = "</span><span class="p">;</span> <span class="n">ofInt</span> <span class="n">this</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">counter</span> <span class="p">]</span> <span class="n">button</span> <span class="p">[</span><span class="nc">OnClick</span> <span class="n">add</span><span class="p">]</span> <span class="p">[</span><span class="n">str</span> <span class="s2">"👍"</span><span class="p">]</span> <span class="p">]</span> <span class="k">let</span> <span class="k">inline</span> <span class="n">counter</span> <span class="bp">()</span> <span class="p">=</span> <span class="n">ofType</span><span class="p"><</span><span class="nc">Counter</span><span class="o">,_,_></span> <span class="n">createEmpty</span> <span class="bp">[]</span> <span class="k">let</span> <span class="n">init</span><span class="bp">()</span> <span class="p">=</span> <span class="nn">ReactDom</span><span class="p">.</span><span class="n">render</span><span class="p">(</span><span class="n">counter</span> <span class="bp">()</span><span class="p">,</span> <span class="n">document</span><span class="p">.</span><span class="n">getElementById</span><span class="p">(</span><span class="s2">"root"</span><span class="o">))</span> </code></pre></div></div> <p>Before: <img src="/assets/fable-react/module-dead.gif" alt="Dead canary" /></p> <p>After: <img src="/assets/fable-react/module-alive.gif" alt="Living canary" /></p> <h2 id="letting-react-concatenate">Letting React concatenate</h2> <p>While it’s only important with very frequent updates a little detail that can be interesting to look at is how strings are concatenated. The 3 choices are (from better to worse performance):</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">override</span> <span class="n">this</span><span class="p">.</span><span class="n">render</span><span class="bp">()</span> <span class="p">=</span> <span class="n">ul</span> <span class="bp">[]</span> <span class="p">[</span> <span class="c1">// React will use it's DOM diffing and provide very fast update</span> <span class="n">li</span> <span class="bp">[]</span> <span class="p">[</span> <span class="n">str</span> <span class="s2">"Hello, "</span><span class="p">;</span> <span class="n">str</span> <span class="n">this</span><span class="p">.</span><span class="n">props</span><span class="p">.</span><span class="n">name</span><span class="p">]</span> <span class="c1">// Javascript concatenation is a also very fast</span> <span class="n">li</span> <span class="bp">[]</span> <span class="p">[</span> <span class="n">str</span> <span class="p">(</span><span class="s2">"Hello, "</span> <span class="o">+</span> <span class="n">this</span><span class="p">.</span><span class="n">props</span><span class="p">.</span><span class="n">name</span><span class="o">)]</span> <span class="c1">// sprintf is more complex and slower, it's perfectly fine for</span> <span class="c1">// elements that don't render very often but it's not free</span> <span class="n">li</span> <span class="bp">[]</span> <span class="p">[</span> <span class="n">str</span> <span class="p">(</span><span class="n">sprintf</span> <span class="s2">"Hello, %s"</span> <span class="n">this</span><span class="p">.</span><span class="n">props</span><span class="p">.</span><span class="n">name</span><span class="o">)]</span> <span class="p">]</span> </code></pre></div></div> <h2 id="thats-all-folks">That’s all folks</h2> <p>Not a lot more to say, avoid <code class="language-plaintext highlighter-rouge">render()</code>, don’t kill the 🐤️🐤️🐤️ in the Fable mine and everything will be fine !</p> <p>Next article will be on Elmish and how it ties with all of that.</p> </article> </li> <li class="post-separator" /> <li class="post"> <div class="post-header"> <h2 class="post-title"> <a class="post-link" href="/2018/02/06/fable-react-1-react-in-fable-land.html">Fable: React you can be proud of ! Part 1: React in Fable land</a> </h2> <span class="post-meta">Feb 6, 2018</span> </div> <article class="post-content"> <p><a href="http://fable.io/">Fable</a> coupled with <a href="https://github.com/fable-compiler/fable-react">Fable.React</a> and <a href="https://fable-elmish.github.io/react/">Fable.Elmish.React</a> are powerful tools to generate javascript applications. But generating good and fast React code is an already complex task that isn’t made simpler by using a different language and a transpiler.</p> <p>In this series of posts I plan to show how some common React constructs are done in F# and what to look for when optimizing them.</p> <p>The posts will be:</p> <ol> <li><a href="/2018/02/06/fable-react-1-react-in-fable-land.html">React in Fable land</a> (This one)</li> <li><a href="/2018/02/08/fable-react-2-optimizing-react.html">Optimizing React</a></li> <li>Applying to Elmish</li> </ol> <h2 id="starting-a-sample-fable-react-project">Starting a sample Fable React project</h2> <p>If you want to try the code for yourself you’ll need a sample</p> <ul> <li>Start with the fable template as in the <a href="https://github.com/fable-compiler/fable2-samples/tree/master/minimal">minimal template</a></li> <li>Run it in watch mode with <code class="language-plaintext highlighter-rouge">npm start</code> and connect to it via <a href="http://localhost:8080/">http://localhost:8080/</a></li> <li>Replace the <code class="language-plaintext highlighter-rouge">div</code> tag in <code class="language-plaintext highlighter-rouge">public/index.html</code> with <code class="language-plaintext highlighter-rouge"><div id="root"></div></code></li> <li>Change the <code class="language-plaintext highlighter-rouge">App.fs</code> file to look like that:</li> </ul> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">module</span> <span class="nc">App</span> <span class="k">open</span> <span class="nn">Fable</span><span class="p">.</span><span class="nc">Core</span> <span class="k">open</span> <span class="nn">Fable</span><span class="p">.</span><span class="nn">Core</span><span class="p">.</span><span class="nc">JsInterop</span> <span class="k">open</span> <span class="nn">Fable</span><span class="p">.</span><span class="nc">Import</span> <span class="k">open</span> <span class="nn">Fable</span><span class="p">.</span><span class="nn">Import</span><span class="p">.</span><span class="nc">Browser</span> <span class="k">open</span> <span class="nn">Fable</span><span class="p">.</span><span class="nn">Import</span><span class="p">.</span><span class="nc">React</span> <span class="k">open</span> <span class="nn">Fable</span><span class="p">.</span><span class="nn">Helpers</span><span class="p">.</span><span class="nc">React</span> <span class="k">open</span> <span class="nn">Fable</span><span class="p">.</span><span class="nn">Helpers</span><span class="p">.</span><span class="nn">React</span><span class="p">.</span><span class="nc">Props</span> <span class="c1">// -- [BEGIN] part to replace</span> <span class="k">let</span> <span class="n">init</span><span class="bp">()</span> <span class="p">=</span> <span class="k">let</span> <span class="n">element</span> <span class="p">=</span> <span class="n">str</span> <span class="s2">"Hello 🌍"</span> <span class="nn">ReactDom</span><span class="p">.</span><span class="n">render</span><span class="p">(</span><span class="n">element</span><span class="p">,</span> <span class="n">document</span><span class="p">.</span><span class="n">getElementById</span><span class="p">(</span><span class="s2">"root"</span><span class="o">))</span> <span class="c1">// -- [END] part to replace</span> <span class="n">init</span><span class="bp">()</span> </code></pre></div></div> <p><img src="/assets/fable-react/hello-world.png" alt="Hello 🌍" /></p> <h2 id="creating-html-elements">Creating HTML elements</h2> <p>As F# doesn’t have any JSX-like transform creating React elements is done as explained in the <a href="https://reactjs.org/docs/react-without-jsx.html">React Without JSX</a> article, except that instead of directly using <code class="language-plaintext highlighter-rouge">createElement</code> a bunch of helpers are available in the <a href="https://github.com/fable-compiler/fable-react/blob/master/src/Fable.React/Fable.Helpers.React.fs"><code class="language-plaintext highlighter-rouge">Fable.Helpers.React</code> module</a>.</p> <p>For HTML elements the resulting syntax is strongly inspired by the <a href="http://elm-lang.org/">Elm</a> one.</p> <p>Here is a small sample of the more common ones :</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">init</span><span class="bp">()</span> <span class="p">=</span> <span class="k">let</span> <span class="n">element</span> <span class="p">=</span> <span class="c1">// Each HTML element has an helper with the same name</span> <span class="n">ul</span> <span class="c1">// The first parameter is the properties of the elements.</span> <span class="c1">// For html elements they are specified as a list and for custom</span> <span class="c1">// elements it's more typical to find a record creation</span> <span class="p">[</span><span class="nc">ClassName</span> <span class="s2">"my-ul"</span><span class="p">;</span> <span class="nc">Id</span> <span class="s2">"unique-ul"</span><span class="p">]</span> <span class="c1">// The second parameter is the list of children</span> <span class="p">[</span> <span class="c1">// str is the helper for exposing a string to React as an element</span> <span class="n">li</span> <span class="bp">[]</span> <span class="p">[</span> <span class="n">str</span> <span class="s2">"Hello 🌍"</span> <span class="p">]</span> <span class="c1">// Helpers exists also for other primitive types</span> <span class="n">li</span> <span class="bp">[]</span> <span class="p">[</span> <span class="n">str</span> <span class="s2">"The answer is: "</span><span class="p">;</span> <span class="n">ofInt</span> <span class="mi">42</span> <span class="p">]</span> <span class="n">li</span> <span class="bp">[]</span> <span class="p">[</span> <span class="n">str</span> <span class="s2">"π="</span><span class="p">;</span> <span class="n">ofFloat</span> <span class="mi">3</span><span class="p">.</span><span class="mi">14</span> <span class="p">]</span> <span class="c1">// ofOption can be used to return either null or something</span> <span class="n">li</span> <span class="bp">[]</span> <span class="p">[</span> <span class="n">str</span> <span class="s2">"🤐"</span><span class="p">;</span> <span class="n">ofOption</span> <span class="p">(</span><span class="nc">Some</span> <span class="p">(</span><span class="n">str</span> <span class="s2">"🔫"</span><span class="o">))</span> <span class="p">]</span> <span class="c1">// And it can also be used to unconditionally return null, rendering nothing</span> <span class="n">li</span> <span class="bp">[]</span> <span class="p">[</span> <span class="n">str</span> <span class="s2">"😃"</span><span class="p">;</span> <span class="n">ofOption</span> <span class="nc">None</span> <span class="p">]</span> <span class="c1">// ofList allows to expose a list to react, as with any list of elements</span> <span class="c1">// in React each need an unique and stable key</span> <span class="p">[</span><span class="mi">1</span><span class="p">;</span><span class="mi">2</span><span class="p">;</span><span class="mi">3</span><span class="p">]</span> <span class="p">|></span> <span class="nn">List</span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="k">fun</span> <span class="n">i</span> <span class="p">-></span> <span class="k">let</span> <span class="n">si</span> <span class="p">=</span> <span class="n">i</span><span class="p">.</span><span class="nc">ToString</span><span class="bp">()</span> <span class="n">li</span> <span class="p">[</span><span class="nc">Key</span> <span class="n">si</span><span class="p">]</span> <span class="p">[</span><span class="n">str</span> <span class="s2">"🎯 "</span><span class="p">;</span> <span class="n">str</span> <span class="n">si</span><span class="o">])</span> <span class="p">|></span> <span class="n">ofList</span> <span class="c1">// fragment is the <Fragment/> element introduced in React 16 to return</span> <span class="c1">// multiple elements</span> <span class="p">[</span><span class="mi">1</span><span class="p">;</span><span class="mi">2</span><span class="p">;</span><span class="mi">3</span><span class="p">]</span> <span class="p">|></span> <span class="nn">List</span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="k">fun</span> <span class="n">i</span> <span class="p">-></span> <span class="k">let</span> <span class="n">si</span> <span class="p">=</span> <span class="n">i</span><span class="p">.</span><span class="nc">ToString</span><span class="bp">()</span> <span class="n">li</span> <span class="bp">[]</span> <span class="p">[</span><span class="n">str</span> <span class="s2">"🎲 "</span><span class="p">;</span> <span class="n">str</span> <span class="n">si</span><span class="o">])</span> <span class="p">|></span> <span class="n">fragment</span> <span class="bp">[]</span> <span class="p">]</span> <span class="nn">ReactDom</span><span class="p">.</span><span class="n">render</span><span class="p">(</span><span class="n">element</span><span class="p">,</span> <span class="n">document</span><span class="p">.</span><span class="n">getElementById</span><span class="p">(</span><span class="s2">"root"</span><span class="o">))</span> </code></pre></div></div> <p><img src="/assets/fable-react/helpers.png" alt="Output of helpers demonstration" /></p> <h2 id="react-components">React components</h2> <p>While it is possible to use React as a templating engine for HTML by using only built-in components, what really unlocks the power of React and where lies the biggest potential for optimization is in its user-defined components.</p> <p>Creating Components in F# is really similar to how they are created in modern JavaScript. The main difference comes when consuming them as we’ll use the <code class="language-plaintext highlighter-rouge">ofType</code> and <code class="language-plaintext highlighter-rouge">ofFunction</code> helpers (Instead of using JSX or <code class="language-plaintext highlighter-rouge">React.createElement</code>).</p> <h3 id="functional-components">Functional Components</h3> <p>The easiest to use F# components are Functional ones, they don’t need a class, a simple function taking props and returning a <code class="language-plaintext highlighter-rouge">ReactElement</code> will do. They can then be created using the <code class="language-plaintext highlighter-rouge">ofType</code> helper.</p> <p>Let’s see how they are created in JavaScript:</p> <div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">Welcome</span><span class="p">(</span><span class="nx">props</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="p"><</span><span class="nt">h1</span><span class="p">></span>Hello, <span class="si">{</span><span class="nx">props</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="p"></</span><span class="nt">h1</span><span class="p">>;</span> <span class="p">}</span> <span class="kd">function</span> <span class="nx">init</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">element</span> <span class="o">=</span> <span class="p"><</span><span class="nc">Welcome</span> <span class="na">name</span><span class="p">=</span><span class="s">"🌍"</span> <span class="p">/>;</span> <span class="nx">ReactDOM</span><span class="p">.</span><span class="nx">render</span><span class="p">(</span><span class="nx">element</span><span class="p">,</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">root</span><span class="dl">"</span><span class="p">));</span> <span class="p">}</span> </code></pre></div></div> <p>And the equivalent in F#:</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="nc">WelcomeProps</span> <span class="p">=</span> <span class="p">{</span> <span class="n">name</span><span class="p">:</span> <span class="kt">string</span> <span class="p">}</span> <span class="k">let</span> <span class="nc">Welcome</span> <span class="p">{</span> <span class="n">name</span> <span class="p">=</span> <span class="n">name</span> <span class="p">}</span> <span class="p">=</span> <span class="n">h1</span> <span class="bp">[]</span> <span class="p">[</span> <span class="n">str</span> <span class="s2">"Hello, "</span><span class="p">;</span> <span class="n">str</span> <span class="n">name</span> <span class="p">]</span> <span class="k">let</span> <span class="k">inline</span> <span class="n">welcome</span> <span class="n">name</span> <span class="p">=</span> <span class="n">ofFunction</span> <span class="nc">Welcome</span> <span class="p">{</span> <span class="n">name</span> <span class="p">=</span> <span class="n">name</span> <span class="p">}</span> <span class="bp">[]</span> <span class="k">let</span> <span class="n">init</span><span class="bp">()</span> <span class="p">=</span> <span class="k">let</span> <span class="n">element</span> <span class="p">=</span> <span class="n">welcome</span> <span class="s2">"🌍"</span> <span class="nn">ReactDom</span><span class="p">.</span><span class="n">render</span><span class="p">(</span><span class="n">element</span><span class="p">,</span> <span class="n">document</span><span class="p">.</span><span class="n">getElementById</span><span class="p">(</span><span class="s2">"root"</span><span class="o">))</span> </code></pre></div></div> <p><img src="/assets/fable-react/components-h1.png" alt="Hello, 🌍" /></p> <p>Some notes:</p> <ul> <li>We had to declare <code class="language-plaintext highlighter-rouge">WelcomeProps</code> while JavaScript could do without</li> <li>Using <code class="language-plaintext highlighter-rouge">sprintf</code> in the F# sample could have seemed natural but using React for it is a lot better on a performance standpoint as we’ll see later.</li> </ul> <p><em>Note: Due to some peculiarities of the Fable transform there can be negative performance impact of using them but they are avoidable if you know what to look for. I’ll detail this some more in the second post</em></p> <h3 id="class-components">Class Components</h3> <p>To create an user-defined component in F# a class must be created that inherit from <code class="language-plaintext highlighter-rouge">Fable.React.Component<'props,'state></code> and implement at least the mandatory <code class="language-plaintext highlighter-rouge">render()</code> method that returns a <code class="language-plaintext highlighter-rouge">ReactElement</code>.</p> <p>Let’s port our “Hello World” Component:</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="nc">WelcomeProps</span> <span class="p">=</span> <span class="p">{</span> <span class="n">name</span><span class="p">:</span> <span class="kt">string</span> <span class="p">}</span> <span class="k">type</span> <span class="nc">Welcome</span><span class="p">(</span><span class="n">initialProps</span><span class="p">)</span> <span class="p">=</span> <span class="k">inherit</span> <span class="nc">Component</span><span class="p"><</span><span class="nc">WelcomeProps</span><span class="p">,</span> <span class="n">obj</span><span class="o">>(</span><span class="n">initialProps</span><span class="p">)</span> <span class="k">override</span> <span class="n">this</span><span class="p">.</span><span class="n">render</span><span class="bp">()</span> <span class="p">=</span> <span class="n">h1</span> <span class="bp">[]</span> <span class="p">[</span> <span class="n">str</span> <span class="s2">"Hello "</span><span class="p">;</span> <span class="n">str</span> <span class="n">this</span><span class="p">.</span><span class="n">props</span><span class="p">.</span><span class="n">name</span> <span class="p">]</span> <span class="k">let</span> <span class="k">inline</span> <span class="n">welcome</span> <span class="n">name</span> <span class="p">=</span> <span class="n">ofType</span><span class="p"><</span><span class="nc">Welcome</span><span class="o">,_,_></span> <span class="p">{</span> <span class="n">name</span> <span class="p">=</span> <span class="n">name</span> <span class="p">}</span> <span class="bp">[]</span> <span class="k">let</span> <span class="n">init</span><span class="bp">()</span> <span class="p">=</span> <span class="k">let</span> <span class="n">element</span> <span class="p">=</span> <span class="n">welcome</span> <span class="s2">"🌍"</span> <span class="nn">ReactDom</span><span class="p">.</span><span class="n">render</span><span class="p">(</span><span class="n">element</span><span class="p">,</span> <span class="n">document</span><span class="p">.</span><span class="n">getElementById</span><span class="p">(</span><span class="s2">"root"</span><span class="o">))</span> </code></pre></div></div> <p><img src="/assets/fable-react/components-h1.png" alt="Hello, 🌍" /></p> <p>Nothing special here, the only gotcha is that the props passed in the primary constructor even though they are in scope in the <code class="language-plaintext highlighter-rouge">render()</code> method should not be used.</p> <h3 id="class-component-with-state">Class Component with state</h3> <p>All features of React are available in Fable and while the more “Functional” approach of re-rendering with new props is more natural using mutable state is totally possible :</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// A pure, stateless component that will simply display the counter</span> <span class="k">type</span> <span class="nc">CounterDisplayProps</span> <span class="p">=</span> <span class="p">{</span> <span class="n">counter</span><span class="p">:</span> <span class="kt">int</span> <span class="p">}</span> <span class="k">type</span> <span class="nc">CounterDisplay</span><span class="p">(</span><span class="n">initialProps</span><span class="p">)</span> <span class="p">=</span> <span class="k">inherit</span> <span class="nc">PureStatelessComponent</span><span class="p"><</span><span class="nc">CounterDisplayProps</span><span class="o">>(</span><span class="n">initialProps</span><span class="p">)</span> <span class="k">override</span> <span class="n">this</span><span class="p">.</span><span class="n">render</span><span class="bp">()</span> <span class="p">=</span> <span class="n">div</span> <span class="bp">[]</span> <span class="p">[</span> <span class="n">str</span> <span class="s2">"Counter = "</span><span class="p">;</span> <span class="n">ofInt</span> <span class="n">this</span><span class="p">.</span><span class="n">props</span><span class="p">.</span><span class="n">counter</span> <span class="p">]</span> <span class="k">let</span> <span class="k">inline</span> <span class="n">counterDisplay</span> <span class="n">p</span> <span class="p">=</span> <span class="n">ofType</span><span class="p"><</span><span class="nc">CounterDisplay</span><span class="o">,_,_></span> <span class="n">p</span> <span class="bp">[]</span> <span class="c1">// Another pure component displaying the buttons</span> <span class="k">type</span> <span class="nc">AddRemoveProps</span> <span class="p">=</span> <span class="p">{</span> <span class="n">add</span><span class="p">:</span> <span class="nc">MouseEvent</span> <span class="p">-></span> <span class="kt">unit</span><span class="p">;</span> <span class="n">remove</span><span class="p">:</span> <span class="nc">MouseEvent</span> <span class="p">-></span> <span class="kt">unit</span> <span class="p">}</span> <span class="k">type</span> <span class="nc">AddRemove</span><span class="p">(</span><span class="n">initialProps</span><span class="p">)</span> <span class="p">=</span> <span class="k">inherit</span> <span class="nc">PureStatelessComponent</span><span class="p"><</span><span class="nc">AddRemoveProps</span><span class="o">>(</span><span class="n">initialProps</span><span class="p">)</span> <span class="k">override</span> <span class="n">this</span><span class="p">.</span><span class="n">render</span><span class="bp">()</span> <span class="p">=</span> <span class="n">div</span> <span class="bp">[]</span> <span class="p">[</span> <span class="n">button</span> <span class="p">[</span><span class="nc">OnClick</span> <span class="n">this</span><span class="p">.</span><span class="n">props</span><span class="p">.</span><span class="n">add</span><span class="p">]</span> <span class="p">[</span><span class="n">str</span> <span class="s2">"👍"</span><span class="p">]</span> <span class="n">button</span> <span class="p">[</span><span class="nc">OnClick</span> <span class="n">this</span><span class="p">.</span><span class="n">props</span><span class="p">.</span><span class="n">remove</span><span class="p">]</span> <span class="p">[</span><span class="n">str</span> <span class="s2">"👎"</span><span class="p">]</span> <span class="p">]</span> <span class="k">let</span> <span class="k">inline</span> <span class="n">addRemove</span> <span class="n">props</span> <span class="p">=</span> <span class="n">ofType</span><span class="p"><</span><span class="nc">AddRemove</span><span class="o">,_,_></span> <span class="n">props</span> <span class="bp">[]</span> <span class="c1">// The counter itself using state to keep the count</span> <span class="k">type</span> <span class="nc">CounterState</span> <span class="p">=</span> <span class="p">{</span> <span class="n">counter</span><span class="p">:</span> <span class="kt">int</span> <span class="p">}</span> <span class="k">type</span> <span class="nc">Counter</span><span class="p">(</span><span class="n">initialProps</span><span class="p">)</span> <span class="k">as</span> <span class="n">this</span> <span class="p">=</span> <span class="k">inherit</span> <span class="nc">Component</span><span class="p"><</span><span class="n">obj</span><span class="p">,</span> <span class="nc">CounterState</span><span class="o">>(</span><span class="n">initialProps</span><span class="p">)</span> <span class="k">do</span> <span class="n">this</span><span class="p">.</span><span class="n">setInitState</span><span class="o">({</span> <span class="n">counter</span> <span class="p">=</span> <span class="mi">0</span><span class="o">})</span> <span class="c1">// This is the equivalent of doing `this.add = this.add.bind(this)`</span> <span class="c1">// in javascript (Except for the fact that we can't reuse the name)</span> <span class="k">let</span> <span class="n">add</span> <span class="p">=</span> <span class="n">this</span><span class="p">.</span><span class="nc">Add</span> <span class="k">let</span> <span class="n">remove</span> <span class="p">=</span> <span class="n">this</span><span class="p">.</span><span class="nc">Remove</span> <span class="k">member</span> <span class="n">this</span><span class="p">.</span><span class="nc">Add</span><span class="o">(_:</span><span class="nc">MouseEvent</span><span class="p">)</span> <span class="p">=</span> <span class="n">this</span><span class="p">.</span><span class="n">setState</span><span class="o">({</span> <span class="n">counter</span> <span class="p">=</span> <span class="n">this</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">counter</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">})</span> <span class="k">member</span> <span class="n">this</span><span class="p">.</span><span class="nc">Remove</span><span class="o">(_:</span><span class="nc">MouseEvent</span><span class="p">)</span> <span class="p">=</span> <span class="n">this</span><span class="p">.</span><span class="n">setState</span><span class="o">({</span> <span class="n">counter</span> <span class="p">=</span> <span class="n">this</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">counter</span> <span class="p">-</span> <span class="mi">1</span> <span class="o">})</span> <span class="k">override</span> <span class="n">this</span><span class="p">.</span><span class="n">render</span><span class="bp">()</span> <span class="p">=</span> <span class="n">div</span> <span class="bp">[]</span> <span class="p">[</span> <span class="n">counterDisplay</span> <span class="p">{</span> <span class="nn">CounterDisplayProps</span><span class="p">.</span><span class="n">counter</span> <span class="p">=</span> <span class="n">this</span><span class="p">.</span><span class="n">state</span><span class="p">.</span><span class="n">counter</span> <span class="p">}</span> <span class="n">addRemove</span> <span class="p">{</span> <span class="n">add</span> <span class="p">=</span> <span class="n">add</span><span class="p">;</span> <span class="n">remove</span> <span class="p">=</span> <span class="n">remove</span> <span class="p">}</span> <span class="p">]</span> <span class="k">let</span> <span class="k">inline</span> <span class="n">counter</span> <span class="n">props</span> <span class="p">=</span> <span class="n">ofType</span><span class="p"><</span><span class="nc">Counter</span><span class="o">,_,_></span> <span class="n">props</span> <span class="bp">[]</span> <span class="c1">// createEmpty is used to emit '{}' in javascript, an empty object</span> <span class="k">let</span> <span class="n">init</span><span class="bp">()</span> <span class="p">=</span> <span class="k">let</span> <span class="n">element</span> <span class="p">=</span> <span class="n">counter</span> <span class="n">createEmpty</span> <span class="nn">ReactDom</span><span class="p">.</span><span class="n">render</span><span class="p">(</span><span class="n">element</span><span class="p">,</span> <span class="n">document</span><span class="p">.</span><span class="n">getElementById</span><span class="p">(</span><span class="s2">"root"</span><span class="o">))</span> </code></pre></div></div> <p><img src="/assets/fable-react/state-counter.gif" alt="Counter = 42" /></p> <p><em>Note: This sample uses a few react-friendly optimizations that will be the subject of the second post.</em></p> <h2 id="thats-all-folks">That’s all folks</h2> <p>Nothing special this time and for anyone that know both React and Fable there was not a lot of new information but we’ll expand it next time !</p> </article> </li> <li class="post-separator" /> <li class="post"> <div class="post-header"> <h2 class="post-title"> <a class="post-link" href="/2017/12/16/hacking-on-ionide.html">Adding our first feature to Ionide</a> </h2> <span class="post-meta">Dec 16, 2017</span> </div> <article class="post-content"> <p><em>This post is part of the <a href="https://sergeytihon.com/2017/10/22/f-advent-calendar-in-english-2017/">F# Advent Calendar in English 2017</a> organized by <a href="https://twitter.com/sergey_tihon">Sergey Tihon</a></em></p> <p><a href="http://ionide.io/">Ionide</a> is now one of the most used F# editing experience but it’s pretty unique as it’s based on an easily extensible editors: <a href="https://code.visualstudio.com/">Visual Studio Code</a>.</p> <p>In the world of VSCode extensibility no arcane knowledge is required, the editor is open source with a well written code base (in <a href="https://www.typescriptlang.org/">TypeScript</a>), the <a href="https://code.visualstudio.com/docs/extensionAPI/overview">plugin API</a> is easy to get into and most plugins are open source too. While the Javascript (Or TypeScript) language might not be to the taste of everyone, we have <a href="http://fable.io/">Fable</a> allowing us to continue to write F# and run in in the Javascript VM.</p> <p>And Ionide is written in Fable, so we’ll use Ionide and VSCode to edit Ionide itself.</p> <p>Let’s implement a very simple feature and then move to a bigger one! The simple feature will be to add a button in the editor when <code class="language-plaintext highlighter-rouge">.fsx</code> files are edited to call the <code class="language-plaintext highlighter-rouge">FSI: Send File</code> command, sending the full file to F# interactive. The more complex one will be to run the file in the terminal.</p> <h2 id="getting-started">Getting started</h2> <p>First you’ll <a href="https://github.com/ionide/ionide-vscode-fsharp/blob/master/CONTRIBUTING.md">need the prerequisites</a> (VScode, F#, dotnet, yarn, node, …) then let’s checkout a commit without the feature, run a first full build and start code :</p> <div class="language-batch highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">git</span> <span class="kd">clone</span> <span class="kd">git</span>@github.com:ionide/ionide<span class="na">-vscode-fsharp</span>.git <span class="nb">cd</span> <span class="kd">ionide</span><span class="na">-vscode-fsharp </span><span class="kd">git</span> <span class="kd">checkout</span> <span class="na">-b </span><span class="kd">having_fun</span> <span class="kd">c0922fc</span> <span class="kd">build</span> <span class="kd">build</span> <span class="kd">code</span> . </code></pre></div></div> <p>Once this first full build is setup well start our developer loop in VS Code:</p> <ul> <li>In the Integrated terminal (<em>View > Integrated Terminal</em> menu) we run <code class="language-plaintext highlighter-rouge">build watch</code> : <br /><img src="/assets/running-fsx/build-watch.png" alt="Build watch running" /></li> <li>In the debug view we select the <code class="language-plaintext highlighter-rouge">Launch Only</code> configuration: <br /><img src="/assets/running-fsx/debug-launch-only.png" alt="Launch only being selected" /></li> <li>Press <code class="language-plaintext highlighter-rouge">F5</code> to start a debug instance of VS Code.</li> </ul> <p>Once started our loop is simple :</p> <ul> <li>Type new buggy code.</li> <li>Wait for the terminal to show that it rebuilt it successfully</li> <li>Move to the debug instance and press <code class="language-plaintext highlighter-rouge">F1</code> to run <code class="language-plaintext highlighter-rouge">Reload Window</code></li> <li>Test</li> <li>Repeat</li> </ul> <p>The loop is fast and while not as good as a browser with auto-reloading, pretty nice to use.</p> <h2 id="a-json-only-feature">A JSON-only feature</h2> <p>Our first target is to get something like that:</p> <p><img src="/assets/running-fsx/send-to.png" alt="Final result" /></p> <p>And we actually don’t need any code to do it, as the command already exists the only thing we need to do is to to change the <code class="language-plaintext highlighter-rouge">release/package.json</code> file. And as added bonus it’s not something that is build but instead used as-is by Code so we don’t even need to wait for the watch build to test it, simply reloading the test instance window will allow us to see our changes.</p> <p>While the file contains the same field as any <code class="language-plaintext highlighter-rouge">package.json</code> file, it’s a section specific to Code that interest us : <code class="language-plaintext highlighter-rouge">contributes</code>. It contains all of the different things that the Ionide extensions declare that it’s able to provide and it’s <a href="https://code.visualstudio.com/docs/extensionAPI/extension-points">well documented on VS Code website</a>.</p> <p>The command we want to run is shown as <code class="language-plaintext highlighter-rouge">FSI: Send File</code> in the command palette but to run it we’ll need it’s ID and that can be found in the <code class="language-plaintext highlighter-rouge">commands</code> section.</p> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nl">"contributes"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"commands"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"fsi.SendFile"</span><span class="p">,</span><span class="w"> </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"FSI: Send File"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div> <p>We currently want to show it in the editor title menu bar that is documented as being the <code class="language-plaintext highlighter-rouge">editor/title</code> section inside <code class="language-plaintext highlighter-rouge">menu</code>, so let’s add it:</p> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nl">"contributes"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"menus"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"editor/title"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"fsi.SendFile"</span><span class="p">,</span><span class="w"> </span><span class="nl">"when"</span><span class="p">:</span><span class="w"> </span><span class="s2">"editorLangId == 'fsharp' && resourceExtname == '.fsx'"</span><span class="p">,</span><span class="w"> </span><span class="nl">"group"</span><span class="p">:</span><span class="w"> </span><span class="s2">"navigation"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">],</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div> <p>A few notes here:</p> <ul> <li><code class="language-plaintext highlighter-rouge">command</code> is the command we wants to run, that we found just before</li> <li><code class="language-plaintext highlighter-rouge">when</code> is the condition for it to appear, we want to be sure that the file is marked as F# (The language ID is auto detected but the user can also change it manually via the selector on the bottom right) and that it’s a script (Sending <code class="language-plaintext highlighter-rouge">.fs</code> files is possible but we don’t want to suggest it so prominently in the UI)</li> <li><code class="language-plaintext highlighter-rouge">group</code> is used to group related commands but <code class="language-plaintext highlighter-rouge">navigation</code> is special as it tell code to make the item visible directly as an icon instead of being a line in the <code class="language-plaintext highlighter-rouge">...</code> menu (the default)</li> </ul> <p><em>Note: <code class="language-plaintext highlighter-rouge">resourceExtname</code> is a feature that was <a href="https://github.com/Microsoft/vscode/pull/34889">added to VS Code</a> by Krzysztof Cieślak, the author of Ionide specifically for this change!</em></p> <p>We reload the window and…</p> <p><img src="/assets/running-fsx/no-icon.png" alt="No icon" /></p> <p>We forgot the icon 😢</p> <p>Turns out that the icon is declared along with the command (And show everywhere the command is shown) instead of being part of the menu, so we’ll get back to the command definition and add the icon</p> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nl">"contributes"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"commands"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"fsi.SendFile"</span><span class="p">,</span><span class="w"> </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"FSI: Send File"</span><span class="p">,</span><span class="w"> </span><span class="nl">"icon"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"light"</span><span class="p">:</span><span class="w"> </span><span class="s2">"./images/send-light.svg"</span><span class="p">,</span><span class="w"> </span><span class="nl">"dark"</span><span class="p">:</span><span class="w"> </span><span class="s2">"./images/send-dark.svg"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div> <p>The images need to be placed in the <code class="language-plaintext highlighter-rouge">release/image</code> folder and to start copying existing ones is good enough. There are 2 images to allow for small variations between light and dark themes, but <code class="language-plaintext highlighter-rouge">icon</code> can also be the path to a single image if the distinction isn’t needed.</p> <p>And finally our feature is here, we can test it and verify that it works as expected, job done 🙌.</p> <p><img src="/assets/running-fsx/send-to.png" alt="Final result" /></p> <h2 id="adding-our-own-command">Adding our own command</h2> <p>Now let’s add another small feature: Running FSX scripts in the terminal</p> <p><img src="/assets/running-fsx/run-icon.png" alt="Menu with run and send" /></p> <p>To do that well go back to <code class="language-plaintext highlighter-rouge">package.json</code> and add our new command:</p> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nl">"contributes"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"commands"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"fsharp.scriptrunner.run"</span><span class="p">,</span><span class="w"> </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"F#: Run script"</span><span class="p">,</span><span class="w"> </span><span class="nl">"icon"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"light"</span><span class="p">:</span><span class="w"> </span><span class="s2">"./images/run-light.svg"</span><span class="p">,</span><span class="w"> </span><span class="nl">"dark"</span><span class="p">:</span><span class="w"> </span><span class="s2">"./images/run-dark.svg"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">],</span><span class="w"> </span><span class="nl">"menus"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"commandPalette"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"fsharp.scriptrunner.run"</span><span class="p">,</span><span class="w"> </span><span class="nl">"when"</span><span class="p">:</span><span class="w"> </span><span class="s2">"editorLangId == 'fsharp' && resourceExtname == '.fsx'"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">],</span><span class="w"> </span><span class="nl">"editor/title"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"fsharp.scriptrunner.run"</span><span class="p">,</span><span class="w"> </span><span class="nl">"when"</span><span class="p">:</span><span class="w"> </span><span class="s2">"editorLangId == 'fsharp' && resourceExtname == '.fsx'"</span><span class="p">,</span><span class="w"> </span><span class="nl">"group"</span><span class="p">:</span><span class="w"> </span><span class="s2">"navigation"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="err">]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div> <p>We’ll also need to add a new file to host our code in <code class="language-plaintext highlighter-rouge">src/Components/ScriptRunner.fs</code> and add it to the <code class="language-plaintext highlighter-rouge">.fsproj</code> file. (After this step the <code class="language-plaintext highlighter-rouge">watch</code> script might need to be restarted manually to pickup the new file)</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">Ionide</span><span class="p">.</span><span class="nn">VSCode</span><span class="p">.</span><span class="nc">FSharp</span> <span class="k">open</span> <span class="nc">System</span> <span class="k">open</span> <span class="nn">Fable</span><span class="p">.</span><span class="nc">Core</span> <span class="k">open</span> <span class="nn">Fable</span><span class="p">.</span><span class="nn">Import</span><span class="p">.</span><span class="n">vscode</span> <span class="k">open</span> <span class="nn">Fable</span><span class="p">.</span><span class="nn">Import</span><span class="p">.</span><span class="nc">Node</span> <span class="k">module</span> <span class="nc">ScriptRunner</span> <span class="p">=</span> <span class="k">let</span> <span class="k">private</span> <span class="n">runFile</span> <span class="bp">()</span> <span class="p">=</span> <span class="n">printfn</span> <span class="s2">"Hello world"</span> <span class="k">let</span> <span class="n">activate</span> <span class="p">(</span><span class="n">context</span><span class="p">:</span> <span class="nc">ExtensionContext</span><span class="p">)</span> <span class="p">=</span> <span class="n">commands</span><span class="p">.</span><span class="n">registerCommand</span><span class="p">(</span> <span class="s2">"fsharp.scriptrunner.run"</span><span class="p">,</span> <span class="n">runFile</span> <span class="p">|></span> <span class="n">unbox</span><span class="p"><</span><span class="nc">Func</span><span class="p"><</span><span class="n">obj</span><span class="p">,</span><span class="n">obj</span><span class="o">>>)</span> <span class="p">|></span> <span class="n">context</span><span class="p">.</span><span class="n">subscriptions</span><span class="p">.</span><span class="nc">Add</span> </code></pre></div></div> <p>Adding the call to activate in <code class="language-plaintext highlighter-rouge">fsharp.fsx</code> is the final touch and our new button will finally do something (Even if it’s only writing “Hello world” to the Debug Console)</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ...</span> <span class="nn">Forge</span><span class="p">.</span><span class="n">activate</span> <span class="n">context</span> <span class="nn">Fsi</span><span class="p">.</span><span class="n">activate</span> <span class="n">context</span> <span class="c1">// <--- Line added here</span> <span class="nn">ScriptRunner</span><span class="p">.</span><span class="n">activate</span> <span class="n">context</span> <span class="p">{</span> <span class="nc">ProjectLoadedEvent</span> <span class="p">=</span> <span class="nn">Project</span><span class="p">.</span><span class="n">projectLoaded</span><span class="p">.</span><span class="k">event</span> <span class="nc">BuildProject</span> <span class="p">=</span> <span class="nn">MSBuild</span><span class="p">.</span><span class="n">buildProjectPath</span> <span class="s2">"Build"</span> <span class="nc">GetProjectLauncher</span> <span class="p">=</span> <span class="nn">Project</span><span class="p">.</span><span class="n">getLauncher</span> <span class="nc">DebugProject</span> <span class="p">=</span> <span class="n">debugProject</span> <span class="p">}</span> </code></pre></div></div> <h3 id="finally-we-write-some-code">Finally, we write some code</h3> <p>Let’s start with the most simple addition, creating a terminal with a fixed name that run <code class="language-plaintext highlighter-rouge">fsi.exe</code> with the current file as single parameter</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="k">private</span> <span class="n">runFile</span> <span class="bp">()</span> <span class="p">=</span> <span class="k">let</span> <span class="n">scriptFile</span> <span class="p">=</span> <span class="n">window</span><span class="p">.</span><span class="n">activeTextEditor</span><span class="p">.</span><span class="n">document</span><span class="p">.</span><span class="n">fileName</span> <span class="k">let</span> <span class="n">terminal</span> <span class="p">=</span> <span class="n">window</span><span class="p">.</span><span class="n">createTerminal</span><span class="p">(</span><span class="s2">"script"</span><span class="p">,</span> <span class="nn">Environment</span><span class="p">.</span><span class="n">fsi</span><span class="p">,</span> <span class="p">[|</span> <span class="n">scriptFile</span><span class="o">|])</span> <span class="n">terminal</span><span class="p">.</span><span class="n">show</span> <span class="bp">()</span> </code></pre></div></div> <p>It works but show a big problem with our approach as it closes immediately the terminal, before we can even read the results of executing our script.</p> <p>A solution to this problem is to run a shell (<code class="language-plaintext highlighter-rouge">cmd.exe</code> for windows) and use it’s capabilities instead of directly starting F# Interactive.</p> <p>Ideally we would use it’s arguments that allow to run commands but windows program arguments function differently than unix platforms: They are a single string and programs are free to parse them as they want. As NodeJS started on unix it’s API wants an array of parameters that are then encoded using the <a href="http://www.daviddeley.com/autohotkey/parameters/parameters.htm#WINARGV">MSVCRT rules</a> but <code class="language-plaintext highlighter-rouge">cmd.exe</code> parameter <code class="language-plaintext highlighter-rouge">/C</code> has a very special handling that doesn’t follow theses conventions and we can’t use it.</p> <p>But VSCode allow us to send simulated keystrokes to the terminal so we’ll use this</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="k">private</span> <span class="n">runFile</span> <span class="bp">()</span> <span class="p">=</span> <span class="k">let</span> <span class="n">scriptFile</span> <span class="p">=</span> <span class="n">window</span><span class="p">.</span><span class="n">activeTextEditor</span><span class="p">.</span><span class="n">document</span><span class="p">.</span><span class="n">fileName</span> <span class="k">let</span> <span class="n">terminal</span> <span class="p">=</span> <span class="n">window</span><span class="p">.</span><span class="n">createTerminal</span><span class="p">(</span><span class="s2">"script"</span><span class="p">,</span> <span class="s2">"cmd.exe"</span><span class="p">,</span> <span class="o">[||])</span> <span class="n">terminal</span><span class="p">.</span><span class="n">sendText</span><span class="p">(</span><span class="n">sprintf</span> <span class="s2">"</span><span class="se">\"</span><span class="s2">%s</span><span class="se">\"</span><span class="s2"> </span><span class="se">\"</span><span class="s2">%s</span><span class="se">\"</span><span class="s2"> && pause && exit"</span> <span class="nn">Environment</span><span class="p">.</span><span class="n">fsi</span> <span class="n">scriptFile</span><span class="p">)</span> <span class="n">terminal</span><span class="p">.</span><span class="n">show</span> <span class="bp">()</span> </code></pre></div></div> <p><img src="/assets/running-fsx/run-first-try.png" alt="First try, working" /></p> <p>We now have a working minimal sample and can start to build from there:</p> <ul> <li>We need to change our directory to the one of the script (the default is the root of the workspace) before running it</li> <li>The fixed title become confusing pretty fast if we run multiple different scripts</li> </ul> <p>For both we might be tempted to use <code class="language-plaintext highlighter-rouge">System.IO</code>, but that’s not something that’s currently translated:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ERROR in ./src/Components/ScriptRunner.fs G:/Code/_ext/ionide-fs/src/Components/ScriptRunner.fs(24,24): (24,67) error FABLE: Cannot find replacement for System.IO.Path::GetDirectoryName @ ./src/fsharp.fs 45:0-71 @ ./src/Ionide.FSharp.fsproj </code></pre></div></div> <p>The solution is to use the <a href="https://nodejs.org/api/path.html">NodeJS Path API</a> directly, the whole NodeJS API is provided by Fable in the <a href="https://www.nuget.org/packages/Fable.Import.Node/">Fable.Import.Node Nuget package</a> that Ionide already import</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">open</span> <span class="nn">Fable</span><span class="p">.</span><span class="nn">Import</span><span class="p">.</span><span class="nc">Node</span> <span class="k">let</span> <span class="k">private</span> <span class="n">runFile</span> <span class="bp">()</span> <span class="p">=</span> <span class="k">let</span> <span class="n">scriptFile</span> <span class="p">=</span> <span class="n">window</span><span class="p">.</span><span class="n">activeTextEditor</span><span class="p">.</span><span class="n">document</span><span class="p">.</span><span class="n">fileName</span> <span class="k">let</span> <span class="n">scriptDir</span> <span class="p">=</span> <span class="nn">Path</span><span class="p">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">scriptFile</span><span class="p">)</span> <span class="k">let</span> <span class="n">title</span> <span class="p">=</span> <span class="nn">Path</span><span class="p">.</span><span class="n">basename</span> <span class="n">scriptFile</span> <span class="k">let</span> <span class="n">terminal</span> <span class="p">=</span> <span class="n">window</span><span class="p">.</span><span class="n">createTerminal</span><span class="p">(</span><span class="n">title</span><span class="p">,</span> <span class="s2">"cmd.exe"</span><span class="p">,</span> <span class="p">[|</span> <span class="s2">"/K"</span> <span class="o">|])</span> <span class="n">terminal</span><span class="p">.</span><span class="n">sendText</span><span class="p">(</span><span class="n">sprintf</span> <span class="s2">"cd </span><span class="se">\"</span><span class="s2">%s</span><span class="se">\"</span><span class="s2"> && </span><span class="se">\"</span><span class="s2">%s</span><span class="se">\"</span><span class="s2"> </span><span class="se">\"</span><span class="s2">%s</span><span class="se">\"</span><span class="s2"> && pause && exit"</span> <span class="n">scriptDir</span> <span class="nn">Environment</span><span class="p">.</span><span class="n">fsi</span> <span class="n">scriptFile</span><span class="p">)</span> <span class="n">terminal</span><span class="p">.</span><span class="n">show</span> <span class="bp">()</span> </code></pre></div></div> <p>Now let’s cleanup, add unix support and we’re ready to send a PR:</p> <div class="language-fsharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">Ionide</span><span class="p">.</span><span class="nn">VSCode</span><span class="p">.</span><span class="nc">FSharp</span> <span class="k">open</span> <span class="nc">System</span> <span class="k">open</span> <span class="nn">Fable</span><span class="p">.</span><span class="nn">Import</span><span class="p">.</span><span class="n">vscode</span> <span class="k">open</span> <span class="nn">Fable</span><span class="p">.</span><span class="nn">Import</span><span class="p">.</span><span class="nc">Node</span> <span class="k">module</span> <span class="nc">ScriptRunner</span> <span class="p">=</span> <span class="k">let</span> <span class="k">private</span> <span class="n">runFile</span> <span class="p">(</span><span class="n">context</span><span class="p">:</span> <span class="nc">ExtensionContext</span><span class="p">)</span> <span class="bp">()</span> <span class="p">=</span> <span class="k">let</span> <span class="n">scriptFile</span> <span class="p">=</span> <span class="n">window</span><span class="p">.</span><span class="n">activeTextEditor</span><span class="p">.</span><span class="n">document</span><span class="p">.</span><span class="n">fileName</span> <span class="k">let</span> <span class="n">scriptDir</span> <span class="p">=</span> <span class="nn">Path</span><span class="p">.</span><span class="n">dirname</span><span class="p">(</span><span class="n">scriptFile</span><span class="p">)</span> <span class="k">let</span> <span class="p">(</span><span class="n">shellCmd</span><span class="p">,</span> <span class="n">shellArgs</span><span class="p">,</span> <span class="n">textToSend</span><span class="p">)</span> <span class="p">=</span> <span class="k">match</span> <span class="nn">Os</span><span class="p">.</span><span class="n">``type``</span><span class="bp">()</span> <span class="k">with</span> <span class="p">|</span> <span class="s2">"Windows_NT"</span> <span class="p">-></span> <span class="p">(</span><span class="s2">"cmd.exe"</span><span class="p">,</span> <span class="p">[|</span> <span class="s2">"/Q"</span><span class="p">;</span> <span class="s2">"/K"</span> <span class="o">|],</span> <span class="n">sprintf</span> <span class="s2">"cd </span><span class="se">\"</span><span class="s2">%s</span><span class="se">\"</span><span class="s2"> && cls && </span><span class="se">\"</span><span class="s2">%s</span><span class="se">\"</span><span class="s2"> </span><span class="se">\"</span><span class="s2">%s</span><span class="se">\"</span><span class="s2"> && pause && exit"</span> <span class="n">scriptDir</span> <span class="nn">Environment</span><span class="p">.</span><span class="n">fsi</span> <span class="n">scriptFile</span><span class="p">)</span> <span class="p">|</span> <span class="p">_</span> <span class="p">-></span> <span class="p">(</span><span class="s2">"sh"</span><span class="p">,</span> <span class="o">[||],</span> <span class="n">sprintf</span> <span class="s2">"cd </span><span class="se">\"</span><span class="s2">%s</span><span class="se">\"</span><span class="s2"> && clear && </span><span class="se">\"</span><span class="s2">%s</span><span class="se">\"</span><span class="s2"> </span><span class="se">\"</span><span class="s2">%s</span><span class="se">\"</span><span class="s2"> && echo </span><span class="se">\"</span><span class="s2">Press enter to close script...</span><span class="se">\"</span><span class="s2"> && read && exit"</span> <span class="n">scriptDir</span> <span class="nn">Environment</span><span class="p">.</span><span class="n">fsi</span> <span class="n">scriptFile</span><span class="p">)</span> <span class="k">let</span> <span class="n">title</span> <span class="p">=</span> <span class="nn">Path</span><span class="p">.</span><span class="n">basename</span> <span class="n">scriptFile</span> <span class="k">let</span> <span class="n">terminal</span> <span class="p">=</span> <span class="n">window</span><span class="p">.</span><span class="n">createTerminal</span><span class="p">(</span><span class="n">title</span><span class="p">,</span> <span class="n">shellCmd</span><span class="p">,</span> <span class="n">shellArgs</span><span class="p">)</span> <span class="n">terminal</span><span class="p">.</span><span class="n">sendText</span><span class="p">(</span><span class="n">textToSend</span><span class="p">)</span> <span class="n">terminal</span><span class="p">.</span><span class="n">show</span> <span class="bp">()</span> <span class="k">let</span> <span class="n">activate</span> <span class="p">(</span><span class="n">context</span><span class="p">:</span> <span class="nc">ExtensionContext</span><span class="p">)</span> <span class="p">=</span> <span class="n">commands</span><span class="p">.</span><span class="n">registerCommand</span><span class="p">(</span> <span class="s2">"fsharp.scriptrunner.run"</span><span class="p">,</span> <span class="n">runFile</span> <span class="p">|></span> <span class="n">unbox</span><span class="p"><</span><span class="nc">Func</span><span class="p"><</span><span class="n">obj</span><span class="p">,</span><span class="n">obj</span><span class="o">>>)</span> <span class="p">|></span> <span class="n">context</span><span class="p">.</span><span class="n">subscriptions</span><span class="p">.</span><span class="nc">Add</span> </code></pre></div></div> <h3 id="conclusion">Conclusion</h3> <p>VS Code an Ionide are the perfect introduction for any programmer that wants to start customizing his tools so don’t hesitate.</p> <p>You’re missing an icon ? Some command would make your life easier ? Fork, Build, Contribute !</p> </article> </li> </ul> <div class="pagination"> <span class="previous">Previous</span><a class="next" href="/page2">Next</a> </div> </div> </div> </div> <!-- Empty --> </body> </html>