Anthony Lloyd
2024-02-20T00:25:32+00:00
http://anthonylloyd.github.io
Anthony Lloyd
High performance SIEVE LRU cache proved correct with CsCheck SampleModelBased and SampleConcurrent
2024-02-20T00:00:00+00:00
http://anthonylloyd.github.io/blog/2024/02/20/sieve
<p><a href="https://cachemon.github.io/SIEVE-website/blog/2023/12/17/sieve-is-simpler-than-lru/">SIEVE</a> is a new type of <a href="https://en.wikipedia.org/wiki/Cache_replacement_policies#LRU">LRU</a> cache.
Due to its simplicity, it offers the chance to create a high performance concurrent cache.</p>
<p>The requirements for this cache are that it is fast, thread-safe and has an atomic GetOrAdd method.
Atomic here means that the valueFactory will only be called once for a key.
Neither <a href="https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2.getoradd?view=net-8.0#system-collections-concurrent-concurrentdictionary-2-getoradd(-0-system-func((-0-1)))">ConcurrentDictionary</a> or <a href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.caching.memory.cacheextensions.getorcreateasync?view=dotnet-plat-ext-8.0">IMemoryCache</a> offer this.
</p>
<p><a href="https://github.com/AnthonyLloyd/CsCheck/tree/master?tab=readme-ov-file#model-based-testing">SampleModelBased</a> and <a href="https://github.com/AnthonyLloyd/CsCheck/tree/master?tab=readme-ov-file#concurrency-testing">SampleConcurrent</a> have been used to prove the implementation is correct.
After some experimenting it became clear that the best way to satisfy the tests was to factor the code into a more generally usable GetOrAddAtomicAsync extension method and a thread-safe cache.</p>
<h2><a name="GetOrAddAtomicAsync" class="anchor" href="#GetOrAddAtomicAsync">GetOrAddAtomicAsync</a></h2>
<img style="border:1px solid black" src="/public/sieve/GetOrAddAtomicAsync.png" title="GetOrAddAtomicAsync"/>
<h2><a name="SieveLruCache" class="anchor" href="#SieveLruCache">SieveLruCache</a></h2>
<img style="border:1px solid black" src="/public/sieve/SieveLruCache.png" title="SieveLruCache"/>
<h2><a name="Testing" class="anchor" href="#Testing">Testing</a></h2>
<img style="border:1px solid black" src="/public/sieve/GetOrAddAtomicAsync_SampleConcurrent.png" title="GetOrAddAtomicAsync_SampleConcurrent"/>
<img style="border:1px solid black" src="/public/sieve/SieveLruCache_Tests.png" title="SieveLruCache Tests"/>
<p>SampleModelBased example failure output:</p>
<img style="border:1px solid black" src="/public/sieve/SampleModelBased.png" title="SampleModelBased Output"/>
<p>SampleConcurrent example failure output:</p>
<img style="border:1px solid black" src="/public/sieve/SampleConcurrent.png" title="SampleConcurrent Output"/>
<p>
<a href="/public/sieve/nsdi24-SIEVE.pdf">SIEVE paper</a><br />
<a href="https://github.com/AnthonyLloyd/CsCheck/blob/master/Tests/Cache.cs">Cache.cs</a><br />
<a href="https://github.com/AnthonyLloyd/CsCheck/blob/master/Tests/CacheTests.cs">CacheTests.cs</a><br />
<a href="https://github.com/AnthonyLloyd/CsCheck/blob/master/Tests/SieveLruCache.cs">SieveLruCache.cs</a><br />
<a href="https://github.com/AnthonyLloyd/CsCheck/blob/master/Tests/SieveLruCacheTests.cs">SieveLruCacheTests.cs</a>
</p>
In Defence of Doubles
2023-12-01T00:00:00+00:00
http://anthonylloyd.github.io/blog/2023/12/01/doubles
<p>I sometimes hear developers say you should always use decimals in financial applications.
This surprises me as I've worked in this area for many years and would rarely if ever recommend using decimals.
I'd argue doubles and fixed-point numbers are more sensible options.
</p>
<p>The fear of doubles originates in part from the following misleading paragraph in the Microsoft <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/floating-point-numeric-types">floating-point numeric types</a> reference.</p>
<img style="border:1px solid black" src="/public/doubles/misleading.png" title="Misleading"/>
<p>See also other example discussions:
<a href="https://stackoverflow.com/questions/3730019/why-not-use-double-or-float-to-represent-currency/3730040#3730040">1</a>,
<a href="https://stackoverflow.com/questions/1008826/what-data-type-should-i-use-to-represent-money-in-c">2</a>,
<a href="https://stackoverflow.com/questions/693372/what-is-the-best-data-type-to-use-for-money-in-c">3</a>.
The implication here is that the required accuracy can only be achieved with decimals, and numeric performance is not often important in financial applications.
These two points couldn't be further from the truth, and I'll aim to demonstrate this in the rest of the post.
</p>
<h2><a name="Accuracy" class="anchor" href="#Accuracy">Accuracy</a></h2>
<p><a href="https://www.nuget.org/packages/CsCheck/">CsCheck</a> can be used to investigate the accuracy of doubles.</p>
<img style="border:1px solid black" src="/public/doubles/precision.png" title="Double Precision"/>
<p>This test models a price from a text feed and tests it agrees with what would be displayed after being stored as a double.
The test demonstrates that doubles can hold and store faithfully numeric data up to 15 significate figures.
This is more than enough for a price in a data model.
Price timeseries would normally be stored as fixed-point values as they efficiently compress.</p>
<img style="border:1px solid black" src="/public/doubles/sum_precision.png" title="Sum Precision"/>
<p>This test investigates the error in double sum calculations by comparing the exact result of a sum of longs.</p>
<img style="border:1px solid black" src="/public/doubles/table.png" title="Sum Table"/>
<p>The table shows the length of random sets of doubles that are still free from rounding error.
The numerical errors come from two components, firstly the fact that double doesn't store the exact number required, and secondly addition isn't an exact calculation.
The second component can be eliminated by using <a href="http://anthonylloyd.github.io/blog/2023/10/09/fsum#Neumaier-Algorithm">NSum</a> covered in a previous post.
<a href="http://anthonylloyd.github.io/blog/2023/10/09/fsum#Neumaier-Algorithm">NSum</a> is nice to have but is generally unnecessary, although I have found this kind of correction to be important in <a href="/blog/2023/11/24/allocate-3">Allocate</a>.</p>
<p>So, we are talking about thousands of doubles totalling many billions before we see a penny rounding issue.</p>
<p>This all makes sense since for example Excel also uses doubles to represent numbers.
You can check this by summing a column of 350 9,999,999,999.99 value cells and comparing it to the product.
If there really was an accuracy problem with doubles being used for storing or calculations financial professionals wouldn't use it.
Or as a start they would ask for the sum implementation to be updated to <a href="http://anthonylloyd.github.io/blog/2023/10/09/fsum#Neumaier-Algorithm">NSum</a> as <a href="https://docs.python.org/3/whatsnew/3.12.html">Python 3.12</a> has recently done.
</p>
<p>There are in fact many other more important sources of error than a few cents in the multiple billions.
Errors in price marks, bid prices vs mid, exchange rate prices for foreign currencies.</p>
<p>You won't see an inconsistent total on a page or screen due to double accuracy as you can't view enough rows.
It's only an imagined or mistaken risk by developers.
Seen total errors can only come from an issue with the methodology such as not rounding after currency conversion.</p>
<p>If these calculations were to be exact to the penny, I would imagine the calculation methods would also become more complicated.
For example, if you want to calculate the correct portfolio values in dollars you would need to sum each currency separately to the portfolio level, perform the currency conversion, round and then use <a href="/blog/2022/09/14/allocate">Allocate</a> to pro-rata back to the positions.
Fees would require similar allocating to be done.
</p>
<h2><a name="Performance" class="anchor" href="#Performance">Performance</a></h2>
<p>Decimals are 128-bit, twice the size of 64-bit doubles that fit in a register.
<a href="https://github.com/AnthonyLloyd/CsCheck#performance-testing">Faster</a> and BenchmarkDotNet reasonably agree that doubles are at least 22 times faster for addition and 35 times faster for multiplication.
I've taken care to try to resolve the actual calculation performance difference minimising the memory access effect.
Before doing this they both gave a range of performance ratios up to around 250 times faster.</p>
<img style="border:1px solid black" src="/public/doubles/performance.png" title="Performance"/>
<p>The performance difference is not surprising as decimals are not supported natively by computer processors.
When memory and CPU are combined for multiple calculations, this can be 50 to 100 times slower for decimals.
They are a bit of a dog.
</p>
<p>Valuation, profit and loss, returns and other accounting values can be coded as single pass sequence calculations from aggregated trade data.
They are fast and can be performed on the fly giving functionality that is highly flexible.
I've built a system like this in the past and covered some of these ideas in an old <a href="/blog/2018/02/01/architecture-data-first">post</a>.
</p>
<p>
This is far from what is usually seen.
The norm is to calculate and store these values, often overnight, and to promote these derived values to actual data.
This results in restricted functionality with clunky adjustment processes.
</p>
<p>Performance of decimals vs doubles does matter.
It turns a few 100 milliseconds on the fly calculation into an unworkable 10 seconds plus.
It effects the architecture and features you can offer.</p>
<h2><a name="Implementation" class="anchor" href="#Implementation">Implementation</a></h2>
<p>There are two key implementation details that simplify handling numeric data in financial applications.</p>
<p>Firstly, eliminating primitive obsession. Longs, doubles, decimals, DateTime etc should not be being passed around.
By creating struct types for quantity, price, trade date, time we can ensure that parsing, conversion, calculation, rounding and display are performed consistently without a performance penalty.</p>
<p>Secondly, a well-defined API boundary means we can store data in whatever format makes the most sense while at the same time loading and displaying in a different format.
For example, quantity should always be stored as an int or long.
A floating-point quantity doesn't make sense as it needs to be a quantised number you can exchange.
The word quantum is derived from the Latin word quanta, which means a quantity.
Dollar quantities should be stored in cents and priced as 0.01 USD.
Multipliers used for loading and display are another important tool.
</p>
<p>Together these mean we are in control and can evolve the system safely.</p>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p>Doubles are the perfect fit for financial applications both in terms of accuracy and performance.
Right-sizing in a flexible system will always give the best results.
</p>
<p>Be a <a href="/blog/2018/02/01/architecture-data-first">data-first architect</a>.
Have performance in mind throughout not just as an afterthought.
</p>
<p>
<a href="https://github.com/AnthonyLloyd/CsCheck/blob/master/Tests/FloatingPointTests.cs">FloatingPointTests.cs</a><br />
<a href="/blog/2018/02/01/architecture-data-first">Data-First Architecture</a><br />
<a href="/blog/2023/10/09/fsum">Full precision floating-point summation in C#</a><br />
</p>
Allocation Algorithms Revisited
2023-11-24T00:00:00+00:00
http://anthonylloyd.github.io/blog/2023/11/24/allocate-3
<p>
I realised recently there is another property allocation algorithms <a href="/blog/2022/09/14/allocate">Error-Minimising</a> and <a href="/blog/2022/10/31/allocate-2">Balinski-Young</a> should have.
Robust allocation algorithms should produce the same results reordered for reordered weights.
This is not only about when we need this property to hold, but also about making sure we have the most canonical algorithms possible.
</p>
<h2><a name="Reordered-Weights-Test" class="anchor" href="#Reordered-Weights-Test">Reordered Weights Test</a></h2>
<p>Using <a href="https://www.nuget.org/packages/CsCheck/">CsCheck</a> it is easy enough to add this to the set of <a href="https://github.com/AnthonyLloyd/CsCheck/blob/master/Tests/Allocator_Tests.cs">allocator tests</a>.</p>
<img style="border:1px solid black" src="/public/allocate/reordered.png" title="Reordered"/>
<p>Ignore for the moment the new long weight versions.</p>
<p><a href="/blog/2022/10/31/allocate-2">Balinski-Young</a> passes the test but <a href="/blog/2022/09/14/allocate">Error-Minimising</a> does not.
It has a rounding issue that produces occasional off by one differences.
Investigating further we find the weight sum is causing the problem.</p>
<h2><a name="Error-Minimising-Algorithm" class="anchor" href="#Error-Minimising-Algorithm">Error Minimising Algorithm</a></h2>
<p>This can be solved by using the FSum algorithm from the previous <a href="/blog/2023/10/09/fsum">post</a> with an additional <a href="https://people.eecs.berkeley.edu/~jrs/papers/robustr.pdf#page=29">compression</a> step.</p>
<img style="border:1px solid black" src="/public/allocate/allocate_final.png" title="Allocate Final"/>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p>We are back to being able to say these two algorithms are the best solutions to the allocation problem.
I can't think of any other invariants the algorithm parameters could be required to satisfy.</p>
<p>If possible, prefer the long weight versions as the summation has no rounding complication and is much simpler.</p>
<p>The only possible way to get to the correct algorithm is by using random testing.
The edge cases are just too infrequent with numeric algorithms to catch with example-based testing.
<a href="/blog/2023/11/13/cscheck-3">CsCheck 3</a> was recently released with improved floating-point number generation and testing.</p>
<p>In the next <a href="/blog/2023/12/01/doubles">post</a> we will investigate recommendations on which data types to use for numeric data.</p>
<p><a href="https://people.eecs.berkeley.edu/~jrs/papers/robustr.pdf#page=29">Compression Algorithm</a><br />
<a href="https://github.com/AnthonyLloyd/CsCheck/blob/master/Tests/Allocator.cs">Allocator.cs</a><br />
<a href="https://github.com/AnthonyLloyd/CsCheck/blob/master/Tests/Allocator_Tests.cs">Allocator_Tests.cs</a></p>
CsCheck 3.0.0 released
2023-11-13T00:00:00+00:00
http://anthonylloyd.github.io/blog/2023/11/13/cscheck-3
<p>Find it on <a href="https://www.nuget.org/packages/CsCheck/3.0.0">NuGet</a> and <a href="https://github.com/AnthonyLloyd/CsCheck">github</a>.</p>
<p>Key improvements:<br/>
<ul>
<li>Improved generation for double, float and decimal. Mix of integer, fraction, exponential and evenly distributed. Much better at finding tricky rounding issues.</li>
<li>Improved Print for double, float and decimal. Displays small, rounded fraction and exponential representation.</li>
<li>Improved min size passing and shrinking performance.</li>
<li>Large performance improvements for Sample and Faster.</li>
<li>Plus, many other improvements to make the API easier to use.</li>
</ul>
It was already the fastest, best at shrinking, and had the most features. Now it's even better.<p>
Full precision floating-point summation in C#
2023-10-09T00:00:00+01:00
http://anthonylloyd.github.io/blog/2023/10/09/fsum
<p><a href="https://people.eecs.berkeley.edu/~jrs/papers/robustr.pdf">Shewchuk</a> proved that the following algorithm using multiple nonoverlapping partial sums exactly represents the sum of a sequence of floating-point values.
The error in the returned sum of the partial sums is less than one <a href="https://en.wikipedia.org/wiki/Unit_in_the_last_place">ulp</a>.
This depends on IEEE-754 arithmetic guarantees.</p>
<h2><a name="TwoSum" class="anchor" href="#TwoSum">TwoSum Algorithm</a></h2>
<p><a href="https://en.wikipedia.org/wiki/2Sum">TwoSum</a> is a floating-point algorithm for computing the exact <a href="https://en.wikipedia.org/wiki/Round-off_error">round-off error</a> in a floating-point addition operation where <span class="math">\(hi + lo = a + b\)</span> exactly.
The <span class="math">\(hi\)</span> and <span class="math">\(lo\)</span> values are nonoverlapping meaning the least significant nonzero bit of <span class="math">\(hi\)</span> is more significant than the most significant nonzero bit of <span class="math">\(lo\)</span>.</p>
<img style="border:1px solid black" src="/public/fsum/twosum.png" title="TwoSum"/>
<h2><a name="Shewchuk-Algorithm" class="anchor" href="#Shewchuk-Algorithm">Shewchuk Algorithm</a></h2>
<p>This algorithm tracks all the "lost digits" as the values are added so that the returned value only has a single rounding.
The inner loop applies <a href="https://en.wikipedia.org/wiki/2Sum">TwoSum</a> hi/lo summation to each partial so that the list remains exact.</p>
<img style="border:1px solid black" src="/public/fsum/fsum.png" title="FSum"/>
<p>A good demonstration of this algorithm is FSum([1e100, 1, -1e100, 1e-100, 1e50, -1, -1e50]) = 1e-100.</p>
<p>The algorithm has been optimised for C# though remains a few times slower than a normal sum.
Holding the hi and lo in variables reduces the number of times the partials span needs to be accessed.</p>
<h2><a name="Neumaier-Algorithm" class="anchor" href="#Neumaier-Algorithm">Neumaier Algorithm</a></h2>
<p>This algorithm is faster but without the full round-off error reduction.</p>
<img style="border:1px solid black" src="/public/fsum/nsum.png" title="NSum"/>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p>An optimized C# implementation of the Shewchuk algorithm for full precision summation has been developed, and also the faster but less accurate Neumaier algorithm.
As always these have been comprehensively tested with <a href="https://github.com/AnthonyLloyd/CsCheck/blob/master/Tests/MathXTests.cs">CsCheck</a>.</p>
<p>Python has a version of the Shewchuk algorithm as <a href="https://docs.python.org/3/library/math.html#math.fsum">fsum</a> in the math module of the standard library.
In the recently released <a href="https://docs.python.org/3/whatsnew/3.12.html">Python 3.12</a> the built-in sum function has been changed to use the <a href="https://en.wikipedia.org/wiki/Kahan_summation_algorithm#Further_enhancements">Neumaier algorithm</a> to reduce numerical error for a smaller performance penalty.</p>
<p>In an upcoming <a href="/blog/2023/11/24/allocate-3">post</a> FSum and NSum will be used to improve the <a href="/blog/2022/09/14/allocate">error minimising allocation algorithm</a>.</p>
<p>Links:<br />
<a href="https://en.wikipedia.org/wiki/2Sum">TwoSum</a><br />
<a href="https://people.eecs.berkeley.edu/~jrs/papers/robustr.pdf">Adaptive Precision Floating-Point Arithmetic and Fast Robust Geometric Predicates</a><br />
<a href="https://github.com/AnthonyLloyd/CsCheck/blob/master/Tests/MathX.cs">C# TwoSum, NSum and FSum</a><br />
<a href="https://docs.python.org/3/library/math.html#math.fsum">Python math.fsum</a><br />
<a href="https://code.activestate.com/recipes/393090/">ASPN cookbook recipes for accurate floating point summation</a><br />
<a href="https://en.wikipedia.org/wiki/Kahan_summation_algorithm#Further_enhancements">Kahan and Neumaier summation algorithms</a>
</p>
Balinski-Young weighted allocation algorithm
2022-10-31T00:00:00+00:00
http://anthonylloyd.github.io/blog/2022/10/31/allocate-2
<p>We will cover an alternative weighted allocation algorithm to the one in the previous <a href="/blog/2022/09/14/allocate">post</a>.
These algorithms are used to allocate an integer total quantity over a set of weights.</p>
<p><a href="https://en.wikipedia.org/wiki/Apportionment_paradox">Balinski and Young</a> proved that no allocation algorithm can be free of perceived unintuitive observations, or paradoxes.</p>
<p>The algorithm in the previous <a href="/blog/2022/09/14/allocate">post</a> is the most fair with minimum error but it does suffer from the Alabama paradox.
This is when for a small increment of the total quantity there can be a decrease in one of the allocated values.</p>
<h2><a name="Balinski-Young-Algorithm" class="anchor" href="#Balinski-Young-Algorithm">Balinski-Young Algorithm</a></h2>
<p>Balinski and Young constructed an <a href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC433936/">algorithm</a> that follows the quota rule (all values are either the floor or ceiling of the unrounded allocation) and is free of the Alabama paradox.</p>
<img style="border:1px solid black" src="/public/allocate/balinski_young.png" title="BalinskiYoung"/>
<p>Again this is tested with <a href="https://www.nuget.org/packages/CsCheck/">CsCheck</a> including a test for the Alabama paradox.</p>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p>The Balinski-Young algorithm is said to be slightly biased towards larger weights.
It can be useful in situations where there is the possibility of small incremental increases to the total quantity.</p>
<p>There is little online about this algorithm (or the one in the previous <a href="/blog/2022/09/14/allocate">post</a>) and many <a href="https://stackoverflow.com/questions/1925691/proportionately-distribute-prorate-a-value-across-a-set-of-values">unanswered</a> <a href="https://stackoverflow.com/questions/9088403/distributing-integers-using-weights-how-to-calculate">questions</a>.
From twitter polls not looking correct, to algorithms with poor properties such as cascade rounding or adjusting the largest weights, there seem to be common software errors in this area.</p>
<p><a href="https://github.com/AnthonyLloyd/CsCheck/blob/master/Tests/Allocator.cs">Allocator.cs</a><br />
<a href="https://github.com/AnthonyLloyd/CsCheck/blob/master/Tests/Allocator_Tests.cs">Allocator_Tests.cs</a></p>
A robust weighted allocation algorithm thanks to CsCheck
2022-09-14T00:00:00+01:00
http://anthonylloyd.github.io/blog/2022/09/14/allocate
<p>Allocating an integer total over a set of weights is surprisingly tricky.
Consider a poll result where round percentages are required to add up to 100.
Or money or shares to be allocated to a number of accounts.</p>
<p>The most common way of solving this is to round the results and then adjust the largest weights for any residual difference.</p>
<p>This can produce nonsensical results. It can lead to larger weights getting a smaller allocation.
For small totals it can result in the largest weight getting no allocation at all.</p>
<p>With the help of <a href="https://www.nuget.org/packages/CsCheck/">CsCheck</a> we will <a href="/blog/2018/03/05/rounding">revisit</a> this algorithm.</p>
<h2><a name="CsCheck" class="anchor" href="#CsCheck">CsCheck</a></h2>
<p>We can create random tests to check for these kinds of issues.
They guide us to the simplest algorithm that satisfy the requirements.
The random test code not only gives us many simple failing examples, but also hints at the solution.</p>
<img style="border:1px solid black" src="/public/allocate/tests.png" title="Tests"/>
<h2><a name="Error-Minimising-Algorithm" class="anchor" href="#Error-Minimising-Algorithm">Error Minimising Algorithm</a></h2>
<p>In this case the solution is an error minimisation algorithm, which then guarantees smaller weights never get larger allocations.</p>
<img style="border:1px solid black" src="/public/allocate/allocate.png" title="Allocate"/>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p>Writing random tests leads to a robust algorithm that covers all possible inputs including negative values.</p>
<p>There were also some nasty precision rounding issues to solve that would be hard to spot using standard unit testing and a handful of examples.</p>
<p><a href="https://github.com/AnthonyLloyd/CsCheck/blob/master/Tests/Allocator.cs">Allocator.cs</a><br />
<a href="https://github.com/AnthonyLloyd/CsCheck/blob/master/Tests/Allocator_Tests.cs">Allocator_Tests.cs</a></p>
Why should you use a random testing library in C#?
2022-03-13T00:00:00+00:00
http://anthonylloyd.github.io/blog/2022/03/13/why-random-test
<h2><a name="Property-based-testing-is-a-functional-programming-thing-that-comes-from-Haskell-isn-t-it" class="anchor" href="#Property-based-testing-is-a-functional-programming-thing-that-comes-from-Haskell-isn-t-it">Property-based testing is a functional programming thing that comes from Haskell isn't it?</a></h2>
<p>Well it did originate with the Haskell library QuickCheck, but there isn't anything particularly functional about random testing.</p>
<p>We should also drop the term property-based. Developers often get stuck trying to think what 'property' their code has.</p>
<p>It's better to think of it as a more automated and powerful way of doing example based testing.
When coming up with a small number of simple examples consider if the test could be written for a range of examples instead.</p>
<p>The advantage is the test will be run for 100 or more examples with varying size.
The developer isn't directly coming up with the examples so they are more of an independent test.
Random testing will come up with quirky examples e.g. empty collections, or things that sum to zero.
Also large examples tend to be more <a href="https://youtu.be/1LNEWF8s1hI?t=2055">efficient at catching bugs</a>.</p>
<p>Random tests are able to make a stronger claim than a test with a few examples.
In fact a test that generates any example and runs for a long time is close to being a proof.
A 'long time' could be 60 seconds in CsCheck since examples are run in parallel by default and often millions can be run in this time.</p>
<p>When a random test finds a bug it will shrink it down to the smallest possible example so you can more easily reproduce and diagnose the problem.
CsCheck is particularly good at this as it's the only library that can always skrink to the simplest example and reproduce it directly.</p>
<h2><a name="Gen-It" class="anchor" href="#Gen-It">Gen It</a></h2>
<p>Instead of coming up with examples we need to create a generator <code>Gen<T></code> for them.
This may sound like a pain, but actually it's really simple with the composable fluent <code>Gen</code> classes in CsCheck, and can be done in one or two lines of code.
The generators created for domain types can be composed and reused across a number of tests e.g. serialization and domain logic.</p>
<p>We start with a highly defaulted generator <code>Gen.Double.Array.List</code> say but may want to be more specific <code>Gen.Double[0.0, 100.0].Array[5].List[1, 10]</code> about the range of values.</p>
<p>Some testing libraries can create the generator for you automatically using reflection but this can lead to a number of bugs for the library author and a lack of control and insight for the library user.
Fluent style composition similar to LINQ is a much more robust and extensible option.</p>
<h2><a name="Some-No-Brainers" class="anchor" href="#Some-No-Brainers">Some No Brainers</a></h2>
<ul>
<li>Serialization - the number of bugs seen in serialization code (looking at you json) is almost criminal given how easy it is to roundtrip test serialization using random testing.</li>
<li>Caches and collections - often a key part of server and client side code these can be tested against a suitable simplified test model.</li>
<li>Calculations and algorithms - often possible to generalize examples for calculations and algorithms and check the result given the input.</li>
<li>Code refactoring - keep a copy of the original code with the test, refactor for simplicity and performance, safe in the knowledge it still produces the same results. Pair with a <code>Faster</code> test to monitor the relative performance over a range of inputs.</li>
<li>Multithreading and concurrency - test on the same object instance across multiple threads and examples. Shrink even works for concurrency testing.</li>
</ul>
<h2><a name="Raise-Your-Game" class="anchor" href="#Raise-Your-Game">Raise Your Game</a></h2>
<p>CsCheck has been used to help test serialization in Microsoft Orleans and Hagar, and high performance immutable collections in ImTools.</p>
<p>It has given me the ability to write safe native code and interop, high performance collections, faster and simpler server code and much more.</p>
<p>I noticed that it increased the quality of solutions I can attempt. It basically raised my game.</p>
Minimum Global
2022-03-08T00:00:00+00:00
http://anthonylloyd.github.io/blog/2022/03/08/minimum-global
<p><a href="https://github.com/MKL-NET/MKL.NET#mklnetoptimization">MKL.NET.Optimization</a> now has a global minimum algorithm.</p>
<p>According to the <a href="https://en.wikipedia.org/wiki/No_free_lunch_theorem">no free lunch theorem</a> it is not possible to have a superior optimisation algorithm when averaged across all possible problems.
With this encouraging input I started to think about the properties that would be useful in a global minimum algorithm.</p>
<p>One idea that came to mind was that a sequence of finer and finer grids of starting points using the local BFGS <code>Minimum</code> function would eventually find the global minimum.
It would also find the global minimum for simpler surfaces earlier in the search sequence.
For performance each iteration's grid of starting points could be run in parallel.</p>
<p>After thinking a lot about what the grid size each iteration should have, I realised it's a bit like a game of battleships where you don't know the size of the ships.
If the local BFGS search is close enough it will find its way to the minima.
So the grid search sequence should try to keep reducing the maximum distance between any point in the space to the closest search point.</p>
<p>The most efficient start is a single search point in the centre of the n dimensional space.
Next are the <span class="math">\(2^n\)</span> points that bisect the diagonals to the centre.
Then the <span class="math">\(2^n\)</span> bisecting points around each of the previous iteration's points and so on.</p>
<p>This sequence has been <a href="https://github.com/MKL-NET/MKL.NET/blob/ccba23d994a6bcc238e26a472faa2539b54a9bba/MKL.NET.Optimization/Optimize.Minimum.cs#L735">implemented</a> as a sequence of <code>MinimumIteration</code> in a sync and async form.
Functions have also been <a href="https://github.com/MKL-NET/MKL.NET/blob/ccba23d994a6bcc238e26a472faa2539b54a9bba/MKL.NET.Optimization/Optimize.Minimum.cs#L805">included</a> with a stopping criteria of time and/or number of same minimum value iterations.</p>
<p>These <a href="https://en.wikipedia.org/wiki/Test_functions_for_optimization">test functions</a> have been used to test the algorithm.
The following results were produced running all problems in parallel with a stopping criteria of 4 same iteration or overall time of 20 minutes.</p>
<pre style="color:white;background:black;font-family:'Fira Code Retina', consolas, monospace;font-size:7.5pt;border-radius:5px">
Running <span style="color:cyan">15</span> (out of <span style="color:cyan">1,526</span>) tests for <span style="color:cyan">1</span> iterations on <span style="color:cyan">15</span> threads.
<span style="color:green">minimum_global.rastrigin</span>
INFO: time = 0.1 next = 0.1 fmin = +1.98992 xmin = [0.9949586378394885, 0.9949586378394885]
INFO: time = 0.1 next = 0.1 fmin = +1.98992 xmin = [0.9949586378394885, 0.9949586378394885]
INFO: time = 0.0 next = 0.2 fmin = +1.98992 xmin = [0.9949586378394885, 0.9949586378394885]
INFO: time = 0.1 next = 0.5 fmin = +0.00000 xmin = [2.386471478765806E-09, 2.386471478765806E-09]
INFO: time = 0.1 next = 0.2 fmin = +0.00000 xmin = [-2.9653389804957554E-10, -4.184096382895113E-10]
INFO: time = 0.4 next = 1.5 fmin = +0.00000 xmin = [-2.9653389804957554E-10, -4.184096382895113E-10]
INFO: time = 1.1 next = 4.6 fmin = +0.00000 xmin = [-2.9653389804957554E-10, -4.184096382895113E-10]
<span style="color:green">minimum_global.ackley</span>
INFO: time = 0.0 next = 0.0 fmin = +0.00000 xmin = [3.3839161472926094E-09, 3.3839161472926094E-09]
INFO: time = 0.0 next = 0.0 fmin = +0.00000 xmin = [3.3839161472926094E-09, 3.3839161472926094E-09]
INFO: time = 0.1 next = 0.2 fmin = +0.00000 xmin = [3.3839161472926094E-09, 3.3839161472926094E-09]
INFO: time = 0.0 next = 0.0 fmin = +0.00000 xmin = [3.3839161472926094E-09, 3.3839161472926094E-09]
<span style="color:green">minimum_global.rosenbrock</span>
INFO: time = 0.1 next = 0.1 fmin = +0.00000 xmin = [0.9999986657753233, 0.9999973809080209]
INFO: time = 0.0 next = 0.0 fmin = +0.00000 xmin = [1.0000001348047385, 1.0000002954781022]
INFO: time = 0.1 next = 0.3 fmin = +0.00000 xmin = [0.9999999491710393, 0.9999998761252349]
INFO: time = 0.1 next = 0.4 fmin = +0.00000 xmin = [0.9999998182525037, 0.9999996318431258]
<span style="color:green">minimum_global.beale</span>
INFO: time = 0.0 next = 0.0 fmin = +0.00000 xmin = [2.9999998294895387, 0.4999999536661821]
INFO: time = 0.0 next = 0.0 fmin = +0.00000 xmin = [2.999999955445457, 0.49999998385430106]
INFO: time = 0.0 next = 0.0 fmin = +0.00000 xmin = [2.9999999941267395, 0.4999999997470144]
INFO: time = 0.1 next = 0.3 fmin = +0.00000 xmin = [2.999999998139205, 0.500000000027005]
<span style="color:green">minimum_global.bukin6</span>
INFO: time = 0.1 next = 0.1 fmin = +0.00860 xmin = [-9.807806209940138, 0.961930622059657]
INFO: time = 0.0 next = 0.0 fmin = +0.00860 xmin = [-9.807806209940138, 0.961930622059657]
INFO: time = 0.0 next = 0.0 fmin = +0.00860 xmin = [-9.807806209940138, 0.961930622059657]
INFO: time = 0.0 next = 0.0 fmin = +0.00670 xmin = [-10.624203064867034, 1.1287369076147096]
INFO: time = 0.3 next = 1.1 fmin = +0.00423 xmin = [-9.687302495526835, 0.9384382962760216]
INFO: time = 0.5 next = 2.0 fmin = +0.00090 xmin = [-10.041136295166881, 1.0082441810048306]
INFO: time = 1.3 next = 5.0 fmin = +0.00090 xmin = [-10.041136295166881, 1.0082441810048306]
INFO: time = 2.3 next = 9.3 fmin = +0.00090 xmin = [-10.041136295166881, 1.0082441810048306]
INFO: time = 8.5 next = 34.0 fmin = +0.00042 xmin = [-9.975240688716555, 0.9950542679811764]
INFO: time = 36.0 next = 143.9 fmin = +0.00042 xmin = [-9.975240688716555, 0.9950542679811764]
INFO: time = 133.0 next = 531.9 fmin = +0.00025 xmin = [-9.998885572489371, 0.9997771269228957]
INFO: time = 332.1 next = 1328.5 fmin = +0.00011 xmin = [-10.004816488523078, 1.0009635296898276]
INFO: time = 686.0 next = 0.0 fmin = +0.00011 xmin = [-10.004816488523078, 1.0009635296898276]
<span style="color:green">minimum_global.levi13</span>
INFO: time = 0.1 next = 0.1 fmin = +1.98303 xmin = [0.005736757039913081, 0.011378532059834648]
INFO: time = 0.0 next = 0.0 fmin = +1.98303 xmin = [0.005736757039913081, 0.011378532059834648]
INFO: time = 0.0 next = 0.0 fmin = +0.00000 xmin = [1.0000000036810488, 1.0000000274074299]
INFO: time = 0.3 next = 1.0 fmin = +0.00000 xmin = [0.9999999994681851, 1.000000004107341]
INFO: time = 0.1 next = 0.4 fmin = +0.00000 xmin = [1.0000000001245366, 1.0000000020245596]
INFO: time = 0.5 next = 1.8 fmin = +0.00000 xmin = [0.9999999999951217, 1.0000000003536822]
<span style="color:green">minimum_global.himmelblau</span>
INFO: time = 0.1 next = 0.1 fmin = +0.00000 xmin = [3.00000002153875, 1.9999999996211888]
INFO: time = 0.0 next = 0.0 fmin = +0.00000 xmin = [-3.7793102435231836, -3.2831859858075685]
INFO: time = 0.0 next = 0.1 fmin = +0.00000 xmin = [-2.805118088674678, 3.1313125182517605]
INFO: time = 0.0 next = 0.1 fmin = +0.00000 xmin = [3.584428340112484, -1.8481265264472644]
<span style="color:green">minimum_global.easom</span>
INFO: time = 0.0 next = 0.0 fmin = -1.00000 xmin = [3.141592671360607, 3.141592671360607]
INFO: time = 0.1 next = 0.1 fmin = -1.00000 xmin = [3.1415926535911787, 3.1415926535911787]
INFO: time = 0.4 next = 1.7 fmin = -1.00000 xmin = [3.1415926535911787, 3.1415926535911787]
INFO: time = 0.2 next = 0.6 fmin = -1.00000 xmin = [3.1415926535911787, 3.1415926535911787]
<span style="color:green">minimum_global.crossintray</span>
INFO: time = 0.0 next = 0.0 fmin = -0.00010 xmin = [0, 0]
INFO: time = 0.0 next = 0.0 fmin = -1.79060 xmin = [4.490999229124073, 4.490999229124073]
INFO: time = 0.0 next = 0.1 fmin = -2.06261 xmin = [1.3494065690571644, 1.3494065690571644]
INFO: time = 0.2 next = 0.8 fmin = -2.06261 xmin = [1.3494065690571644, 1.3494065690571644]
INFO: time = 0.1 next = 0.4 fmin = -2.06261 xmin = [-1.3494066323708354, -1.3494066323708354]
INFO: time = 0.7 next = 2.7 fmin = -2.06261 xmin = [-1.3494066323708354, -1.3494066323708354]
<span style="color:green">minimum_global.eggholder</span>
INFO: time = 0.1 next = 0.1 fmin = -66.84372 xmin = [8.456934407364127, 15.650918244004314]
INFO: time = 0.1 next = 0.1 fmin = -559.78686 xmin = [-242.97925178232745, 274.38032908558876]
INFO: time = 0.1 next = 0.6 fmin = -935.33795 xmin = [439.4809966756544, 453.9774342118226]
INFO: time = 0.1 next = 0.6 fmin = -935.33795 xmin = [439.4809966756544, 453.9774342118226]
INFO: time = 0.5 next = 2.2 fmin = -957.12165 xmin = [511.9999999970895, 405.7134987637949]
INFO: time = 0.8 next = 3.4 fmin = -959.64064 xmin = [511.9999999875001, 404.22703322635886]
INFO: time = 1.8 next = 7.3 fmin = -959.64066 xmin = [511.9999999855185, 404.23305453191966]
INFO: time = 3.9 next = 15.4 fmin = -959.64066 xmin = [511.999999990171, 404.2318044375143]
INFO: time = 16.1 next = 64.2 fmin = -959.64066 xmin = [511.999999990171, 404.2318044375143]
INFO: time = 88.3 next = 353.0 fmin = -959.64066 xmin = [511.9999999949194, 404.23182568889445]
<span style="color:green">minimum_global.holder</span>
INFO: time = 0.0 next = 0.0 fmin = -1.73297 xmin = [1.2626272073096718, 0]
INFO: time = 0.1 next = 0.1 fmin = -11.06955 xmin = [4.853855860531211, 9.702113907125167]
INFO: time = 0.0 next = 0.1 fmin = -11.06955 xmin = [-4.8538558433843315, -9.702113889157784]
INFO: time = 0.1 next = 0.5 fmin = -19.20850 xmin = [8.055023477564395, 9.664589973778838]
INFO: time = 0.3 next = 1.3 fmin = -19.20850 xmin = [-8.055023496727005, -9.664589995913872]
INFO: time = 0.7 next = 2.6 fmin = -19.20850 xmin = [8.055023454533762, 9.664590005300033]
INFO: time = 1.2 next = 4.9 fmin = -19.20850 xmin = [-8.055023468586946, -9.664590020713243]
<span style="color:green">minimum_global.mccormick</span>
INFO: time = 0.0 next = 0.0 fmin = -1.91322 xmin = [-0.5471975842759544, -1.5471975714832213]
INFO: time = 0.1 next = 0.1 fmin = -1.91322 xmin = [-0.547197557916689, -1.5471975289171152]
INFO: time = 0.1 next = 0.4 fmin = -1.91322 xmin = [-0.547197552156416, -1.547197551042736]
INFO: time = 0.0 next = 0.0 fmin = -1.91322 xmin = [-0.547197552156416, -1.547197551042736]
<span style="color:green">minimum_global.schaffer2</span>
INFO: time = 0.3 next = 0.3 fmin = +0.23509 xmin = [19.335179384482107, -3.0155700873323715E-06]
INFO: time = 329.9 next = 329.9 fmin = +0.23509 xmin = [19.335179384482107, -3.0155700873323715E-06]
INFO: time = 869.9 next = 0.0 fmin = +0.14225 xmin = [1.2534911725942382E-08, -13.498590702548569]
<span style="color:green">minimum_global.schaffer4</span>
INFO: time = 0.2 next = 0.2 fmin = +0.29258 xmin = [1.2531318244654337, -1.05369468038922E-06]
INFO: time = 91.8 next = 91.8 fmin = +0.29258 xmin = [1.2531318284190727, -1.1098138909324352E-07]
INFO: time = 1108.0 next = 0.0 fmin = +0.29258 xmin = [1.2531318284190727, -1.1098138909324352E-07]
<span style="color:green">minimum_global.styblinski_tang</span>
INFO: time = 0.1 next = 0.1 fmin = -78.33233 xmin = [-2.903534001415568, -2.903534001415568]
INFO: time = 0.0 next = 0.0 fmin = -78.33233 xmin = [-2.9035340256020716, -2.9035340256020716]
INFO: time = 0.0 next = 0.1 fmin = -78.33233 xmin = [-2.9035340256020716, -2.9035340256020716]
INFO: time = 0.0 next = 0.0 fmin = -78.33233 xmin = [-2.9035340256020716, -2.9035340256020716]
Maximum memory usage was <span style="color:cyan">12,512</span> KB (limit set at <span style="color:cyan">102,400</span> KB).
<span style="color:cyan">15</span> tests run in <span style="color:cyan">1,200</span> seconds: <span style="color:cyan">15</span> passed, <span style="color:cyan">0</span> failed, <span style="color:cyan">0</span> skipped. <span style="color:green">Success!</span>
</pre>
<p><a href="https://github.com/MKL-NET/MKL.NET#mklnetoptimization">MKL.NET.Optimization</a> now covers the majority of <a href="https://docs.scipy.org/doc/scipy/reference/optimize.html">scipy.optimize</a> functionality with high performance algorithms. Version 1.0.0 has been released.</p>
Statistic Estimators
2021-11-01T00:00:00+00:00
http://anthonylloyd.github.io/blog/2021/11/01/statistic-estimators
<p>The following are a family of fixed low memory, high performance statistic estimators.</p>
<ul>
<li><a href="https://github.com/MKL-NET/MKL.NET/blob/051a853284e5d8f784db989b27e45d64255a402a/MKL.NET.Statistics/MomentEstimator.cs#L20">Moment4Estimator</a> - Mean, Standard Deviation, Skewness and Kurtosis unbiased estimator.</li>
<li><a href="https://github.com/MKL-NET/MKL.NET/blob/051a853284e5d8f784db989b27e45d64255a402a/MKL.NET.Statistics/MomentEstimator.cs#L93">Moment3Estimator</a> - Mean, Standard Deviation and Skewness unbiased estimator.</li>
<li><a href="https://github.com/MKL-NET/MKL.NET/blob/051a853284e5d8f784db989b27e45d64255a402a/MKL.NET.Statistics/MomentEstimator.cs#L157">Moment2Estimator</a> - Mean and Standard Deviation unbiased estimator.</li>
<li><a href="https://github.com/MKL-NET/MKL.NET/blob/051a853284e5d8f784db989b27e45d64255a402a/MKL.NET.Statistics/MomentEstimator.cs#L212">Moment1Estimator</a> - Mean estimator.</li>
<li><a href="https://github.com/MKL-NET/MKL.NET/blob/master/MKL.NET.Statistics/QuantileEstimator.cs">QuantileEstimator</a> - Single quantile p estimator using min, p/2, p, (1+p)/2, max estimation points.</li>
<li><a href="https://github.com/MKL-NET/MKL.NET/blob/master/MKL.NET.Statistics/QuartileEstimator.cs">QuartileEstimator</a> - Median, Min, Max, Lower Quartile and Upper Quartile estimator. This is the data required to produce a box plot.</li>
<li><a href="https://github.com/MKL-NET/MKL.NET/blob/master/MKL.NET.Statistics/QuantilesEstimator.cs">QuantilesEstimator</a> - Multiple quantile estimator with min and max points.</li>
<li><a href="https://github.com/MKL-NET/MKL.NET/blob/master/MKL.NET.Statistics/HistogramEstimator.cs">HistogramEstimator</a> - Histogram estimator by equal quantile range bucketing.</li>
</ul>
<p>They can incrementally add observations and statistics can be calculated at any point.
All have an Add and + operator overload to combined the summary statistics from two separate estimators.
They are self contained classes that can be copied easily.</p>
<h2><a name="Moment-Estimators" class="anchor" href="#Moment-Estimators">Moment Estimators</a></h2>
<p>The moment estimators are a performance optimised set of unbiased central and standard moment calculations.
The algorithms used are detailed <a href="https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance">here</a>.
Nothing radical just performance optimised code to the specific statistics required.</p>
<h2><a name="P-paper" class="anchor" href="#P-paper">P² paper</a></h2>
<p><a href="/public/stats/psqr.pdf">This</a> paper by Dr Raj Jain and Dr Imrich Chlamtac describes a great way to estimate ordered statistics like median and quantiles without storing all the observations.
The remaining estimators are based on this paper.</p>
<p>Unfortunately there are some problems with the algorithms put forward in the paper which need correcting:</p>
<ol>
<li>The marker increments added each iteration create a <a href="https://aakinshin.net/posts/p2-quantile-estimator-rounding-issue/">rounding error</a> which is important since the marker is compared to integer counts.</li>
<li>The inequality operators used on the quantile values do not agree with the definition of the <a href="https://en.wikipedia.org/wiki/Cumulative_distribution_function">cumulative distribution function</a>.</li>
<li>The Piecewise-Parabolic (P²) interpolation used can produce poor results as the fitted quadratic is not <a href="https://en.wikipedia.org/wiki/Monotonic_function">monotonic</a>.</li>
</ol>
<img style="border:1px solid black" src="/public/interp/quad_over.png" title="Quadratic"/>
<p>Here the dotted line is the fitted quadratic around the middle point that needs to move from 3 to 4.</p>
<p>To correct 1. we cannot use increments and need to just calculate the desired marker position.
Increments were suggested to reduce CPU overhead, but from performance testing in .NET this is not the case.</p>
<p>To correct 2. we need to compare if the observations are less than <strong>or equal</strong> to the quantile value as per the <a href="https://en.wikipedia.org/wiki/Cumulative_distribution_function">CDF</a> definition.
We also need to allow the possibility that there are more than one observation of the minimum value.
Some distributions can produce duplicate values and also a large number of the minimum value.</p>
<p>To correct 3. we need to replace the interpolation.
The issue is not that the quadratic can overshoot since the algorithm checks this and falls back to linear interpolation.
The problem is that it can produce results that are arbitrarily close to one of the points it is next to.</p>
<h2><a name="PCHIP-interpolation" class="anchor" href="#PCHIP-interpolation">PCHIP interpolation</a></h2>
<p>Fortunately there is a monotonic interpolation function that works well and is quick to calculate.</p>
<p><a href="/public/interp/pchip/A_method_for_constructing_local_monotone_picewise_cubic_interpolants_fritsch1984.pdf">PCHIP</a>, or harmonic spline, is a cubic spline interpolation algorithm.
It has been implemented in a number of places such as <a href="https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.PchipInterpolator.html">SciPy</a> and <a href="https://github.com/mathnet/mathnet-numerics/blob/67f3675f1fae7d708587204e1312bf7588c39bca/src/Numerics/Interpolation/CubicSpline.cs#L306">MathNet</a>.</p>
<p><a href="/public/interp/pchip/A_method_for_constructing_local_monotone_picewise_cubic_interpolants_fritsch1984.pdf">PCHIP</a> is very useful for modelling <a href="/public/finance/Stable Interpolation for the Yield Curve - Fabien Le Floc'h.pdf">yield curves</a> as it produces a visually pleasing, stable local interpolation.</p>
<p>For the estimators the <a href="/public/interp/pchip/A_method_for_constructing_local_monotone_picewise_cubic_interpolants_fritsch1984.pdf">PCHIP</a> interpolation actually simplifies as the movement is always by one which removes some terms from the calculation, and the data always has increasing (and not equal) x and y values which removes some testing and branching.</p>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p>The memory requirement for these estimators is small and fixed, regardless of the number of observations.</p>
<p>Performance has been optimised with <a href="https://github.com/MKL-NET/MKL.NET/blob/master/MKL.NET.Statistics/QuartileEstimator.cs">QuartileEstimator</a> being around 40% faster than other more faithful implementations.</p>
<img style="border:1px solid black" src="/public/interp/estimator_perf.png" title="Performance"/>
<p>The efficiency and low memory of these estimators makes them useful for a wide range of uses from performance measurement as above to monitoring and telemetry more generally.</p>
<p>Here are the <a href="https://github.com/MKL-NET/MKL.NET/tree/master/MKL.NET.Statistics">code</a> and <a href="https://github.com/MKL-NET/MKL.NET/blob/master/Tests/Stats.EstimatorTests.fs">tests</a>.</p>
An improved N-dimensional optimiser
2021-10-31T00:00:00+01:00
http://anthonylloyd.github.io/blog/2021/10/31/n-dimensional-optimiser
<p>I realise I didn't blog about this N-dimensional optimiser when it was released so here is a quick overview.</p>
<p>In one dimension a more efficient Minimum algorithm was developed using a robust cubic interpolation.
This has around 50% fewer function calls than Brent across a number of test functions.</p>
<p>In N dimensions an efficient <a href="https://en.wikipedia.org/wiki/Broyden%E2%80%93Fletcher%E2%80%93Goldfarb%E2%80%93Shanno_algorithm">BFGS</a> algorithm was developed using in place symmetric MKL rank-k, rank-2k functions such that the main loop has no allocations.
This combined with the above line minimisation results in around 50-70% fewer function calls than other <a href="https://en.wikipedia.org/wiki/Broyden%E2%80%93Fletcher%E2%80%93Goldfarb%E2%80%93Shanno_algorithm">BFGS</a> algorithms across a number of test functions.
The performance of the algorithm should also scale well with N.</p>
<p>Tolerance parameters across all of the optimisation functions have been made simple, intuative, and consistent.</p>
<p>Here are the <a href="https://github.com/MKL-NET/MKL.NET/blob/master/MKL.NET.Optimization/Optimize.Minimum.cs">code</a> and <a href="https://github.com/MKL-NET/MKL.NET/blob/master/Tests/Optimize.MinimumTests.fs">tests</a>.</p>
An improved root-finding method
2021-06-03T00:00:00+01:00
http://anthonylloyd.github.io/blog/2021/06/03/Root-finding
<p>This method uses two ways to reduce the number of functions calls needed for root-finding.
The first is to create a hybrid of <a href="https://en.wikipedia.org/wiki/Brent%27s_method">Brent's method</a> and
<a href="https://en.wikipedia.org/wiki/Muller%27s_method">Muller's method</a>.
The second is to start with points inside the given boundary instead of evaluating the boundary straight away.</p>
<h2><a name="Hybrid" class="anchor" href="#Hybrid">Hybrid</a></h2>
<p><a href="https://en.wikipedia.org/wiki/Brent%27s_method">Brent's method</a> is a hybrid root-finding algorithm combining inverse quadratic interpolation, linear interpolation and the bisection method.
It has multiple conditions that are used to decide between the potentially fast-converging inverse quadratic interpolation or linear interpolation, or fall back to the more robust bisection method.
These are quite tricky to correctly implement and most stick to standard sources such as "Algorithms for Minimization without Derivatives" by Richard Brent or "Numerical Recipes".</p>
<p><a href="https://en.wikipedia.org/wiki/Muller%27s_method">Muller's method</a> uses quadratic interpolation recursively.</p>
<p>Our hybrid method combines quadratic interpolation, inverse quadratic interpolation and the bisection method (if either of the interpolations do not fall within the bound region then linear interpolation is used).
Our condition for moving on to the next method is simply if the bound region is being reduced by less than 40%.
If greater we return to the quadratic interpolation level. Bisection will always return to this level also.
The rough logic of the algorithm is that generally quadratic is the most useful interpolation but if
it under or over shoots then inverse quadratic could do the opposite and fix any problem with the convergence, falling back to bisection as Brent does.</p>
<img style="border:1px solid black" src="/public/root/RootInner.png" title="RootInner"/>
<h2><a name="Boundary" class="anchor" href="#Boundary">Boundary</a></h2>
<p><a href="https://en.wikipedia.org/wiki/Brent%27s_method">Brent's method</a> first evaluates the given boundary points.</p>
<p>Our method evaluates points 20% in from the boundary.
If the root is bound by these points then we have successfully cut the bound region by 40%.
If not then by looking at the function values we should have a good indication which of the 20% end regions to test.
As a further optimisation our method uses linear interpolation to find a point estimate in these end regions and
tests a point 20% further towards the boundary in the hope to bound the root in a much reduced region.
Our method falls back to testing a boundary point if all this fails.</p>
<p>There is a balance between the size of the inner region and the boundary regions. Too close to boundary and the
inner region is not so much of a win, and too far in and we likely end up evaluating the boundary with other
wasted evaluations along the way.</p>
<p>20% was found to be about right from the test problems.</p>
<h2><a name="Numerical-Results" class="anchor" href="#Numerical-Results">Numerical Results</a></h2>
<p>The 154 test problems from <a href="/public/root/1995_Algorithm_748_Enclosing_Zeros_of_Continuous_Functions.pdf">Enclosing Zeros of Continuous Functions</a>
were used to compare the root-finding methods.</p>
<p>The total number of functions calls are:</p>
<table>
<thead>
<tr class="header">
<th align="center"><p>Tol</p></th>
<th align="center"><p>Brent</p></th>
<th align="center"><p>Hybrid</p></th>
<th align="center"><p>Overall</p></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="center"><p>1e-6</p></td>
<td align="center"><p>2763</p></td>
<td align="center"><p>2446</p></td>
<td align="center"><p>2144</p></td>
</tr>
<tr class="even">
<td align="center"><p>1e-7</p></td>
<td align="center"><p>2816</p></td>
<td align="center"><p>2544</p></td>
<td align="center"><p>2237</p></td>
</tr>
<tr class="odd">
<td align="center"><p>1e-9</p></td>
<td align="center"><p>2889</p></td>
<td align="center"><p>2592</p></td>
<td align="center"><p>2292</p></td>
</tr>
<tr class="even">
<td align="center"><p>1e-11</p></td>
<td align="center"><p>2935</p></td>
<td align="center"><p>2632</p></td>
<td align="center"><p>2329</p></td>
</tr>
</tbody>
</table>
<p>Each of our improvements gives about a 10% reduction in the number of function calls resulting in an overall 20% improvement.</p>
<p>Our method was also tested on bond spread and option volatility problems which compare favourably to Brent.</p>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p>The boundary improvement has been extended to allow the caller to set these inner evaluation points.
This is very useful when you have a good idea of the area the solution should be in from say a previous calibration result.
This additional reduction of calls for prior information is demonstrated in the bond spread and option volatility tests.</p>
<p>A 20% reduction in function calls is a great result but we must consider we may have overfitted to this problem set.
The bond spread and option volatility give some comfort this result holds more generally but further testing
on other problems is needed.</p>
<p>The code is easy to follow and it is obvious a solution will always be found.
There may be further optimisations or features that can be added.</p>
<p>The code and tests can be found <a href="https://github.com/AnthonyLloyd/CsCheck/blob/master/Tests/SolveRootTests.cs">here</a>.</p>
Bitemporal Source Generator Demo
2021-04-28T00:00:00+01:00
http://anthonylloyd.github.io/blog/2021/04/28/Bitemporal
<p>A bitemporal demo of 341 investment funds over 10 months using C# source generator to generate the data model from a schema.</p>
<p>See the <a href="https://github.com/AnthonyLloyd/Bitemporal">demo</a> in github.</p>
<img style="border:1px solid black" src="/public/bitemporal/Bitemporal.png" title="Bitemporal"/>
<h2><a name="How-to-run-the-demo" class="anchor" href="#How-to-run-the-demo">How to run the demo</a></h2>
<table class="pre"><tr><td class="snippet"><pre class="fssnip"><code lang="powershell">dotnet run -p Demo -c Release
</code></pre></td></tr></table>
<p>or for 32-bit</p>
<table class="pre"><tr><td class="snippet"><pre class="fssnip"><code lang="powershell">dotnet run -p Demo -r win-x86 -c Release
</code></pre></td></tr></table>
<h2><a name="Source-Generator" class="anchor" href="#Source-Generator">Source Generator</a></h2>
<p>A bitemporal source generator is created to read a schema file and create the entity types, collections and properties. It provides a good way of generating a large amount of boilerplate code to give a type safe data model.</p>
<p>Groups of edits are committed as transactions. In this case there is one transaction per position file and the filename is stored in the Tx entity. The data model can be queried at a Snapshot of any transaction id and then at any reporting date.</p>
<p>Source generators are much improved on previous code generation techniques. The best way I've found to write them is to start with an example of the required code and lay it out as a multiline string using StringBuilder with spacing, tabs and returns exactly as the output code would be formatted. Then parts of the code can be replaced with variables and templated as needed. This makes it easy to read both types of code in the <a href="Bitemporal.SourceGenerator/BitemporalGenerator.cs">generator</a>.</p>
<h2><a name="DataSeries" class="anchor" href="#DataSeries">DataSeries</a></h2>
<p>DataSeries is an immutable collection representing an ordered table of Reporting Date, Transaction Id (TxId) and Varint (Vint) encoded Values with the latest at the top.</p>
<p>Subsequent rows are stored as the difference to the prior values stored as Vints. With <a href="https://stackoverflow.com/questions/4533076/google-protocol-buffers-zigzag-encoding">zigzag encoding</a> being used for TxId and Vint as the differences can be positive or negative.</p>
<p>DataSeries are represented internally as a byte array and provide both small size and quick look up for any transaction and reporting date.</p>
<p>This forms a <a href="https://martinfowler.com/articles/bitemporal-history.html">bitemporal</a> representation of each value in the database.</p>
<table>
<thead>
<tr class="header">
<th align="center"><p>Date</p></th>
<th align="center"><p>TxId</p></th>
<th align="center"><p>Vint</p></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="center"><p>2016-05-21</p></td>
<td align="center"><p>1234</p></td>
<td align="center"><p>100</p></td>
</tr>
<tr class="even">
<td align="center"><p>2016-05-19</p></td>
<td align="center"><p>1201</p></td>
<td align="center"><p>101</p></td>
</tr>
<tr class="odd">
<td align="center"><p>2016-05-18</p></td>
<td align="center"><p>1229</p></td>
<td align="center"><p>103</p></td>
</tr>
<tr class="even">
<td align="center"><p>2016-05-18</p></td>
<td align="center"><p>1189</p></td>
<td align="center"><p>98</p></td>
</tr>
</tbody>
</table>
<p>DataSeries can also represent sets well as a bitemporal sequence of add and removes.</p>
<h2><a name="Fixed" class="anchor" href="#Fixed">Fixed</a></h2>
<p>These are a set of fixed decimal place numbers represented internally as a long.
As well as having no rounding error they also compress well in the DataSeries for price and quantity time series.</p>
<h2><a name="Slim-Collections" class="anchor" href="#Slim-Collections">Slim Collections</a></h2>
<p>Bitemporal uses ListSlim, SetSlim, and MapSlim. These are increased performance, reduced memory, append only versions of common collections. Thay also have a useful property that they can be used lock free for reading while a write is being performed.</p>
<p>This plus the transactional data model means concurrency is very simple and reads are self-consistent.</p>
<h2><a name="Size" class="anchor" href="#Size">Size</a></h2>
<p>The database is loaded from 4.1 GB of around 70,000 position files for a large asset manager downloaded from their public website over 10 months.</p>
<p>The database is 63 MB saved to disk, 100 MB process memory size for 32-bit, and 130 MB for 64-bit.</p>
<p>This is small enough to commit to github for the demo and shows the potential to store many years of data for any asset manager or similar in memory easily.</p>
Median and MAD Revisited with an Online Estimator
2020-04-07T00:00:00+01:00
http://anthonylloyd.github.io/blog/2020/04/07/Median-Revisited
<p>In a previous <a href="/blog/2016/10/21/MAD-Outliers">post</a> a faster <a href="https://en.wikipedia.org/wiki/Selection_algorithm">Selection</a> algorithm was demonstrated.
This will be revisited focusing on the Median and MAD statistical measures and their practical use in performance testing.</p>
<p>Though the Median is robust to outliers and a very useful measure, one downside is it requires all the sample values to be kept for the calculation.
For <a href="https://en.wikipedia.org/wiki/Online_algorithm">online</a> algorithms as the sample size increases this creates a memory problem.
An online algorithm will be provided to estimate the Median and MAD in fixed memory.</p>
<h2><a name="Exact-Median-Algorithm" class="anchor" href="#Exact-Median-Algorithm">Exact Median Algorithm</a></h2>
<p>This algorithm is based on the <a href="http://zabrodskyvlada.byethost10.com/aat/a_modi.html">MODIFIND</a> algorithm by Vladimir Zabrodsky.
In the previous <a href="/blog/2016/10/21/MAD-Outliers">post</a> it was shown to behave well with partially sorted and duplicate data.</p>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="k">module</span> <span class="m">Statistics</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs1', 1)" onmouseover="showTip(event, 'fs1', 1)" class="fn">medianInPlace</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs2', 2)" onmouseover="showTip(event, 'fs2', 2)" class="id">a</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs3', 3)" onmouseover="showTip(event, 'fs3', 3)" class="vt">float</span><span class="pn">[</span><span class="pn">]</span><span class="pn">)</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs4', 4)" onmouseover="showTip(event, 'fs4', 4)" class="id">index</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs5', 5)" onmouseover="showTip(event, 'fs5', 5)" class="vt">int</span><span class="pn">)</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs6', 6)" onmouseover="showTip(event, 'fs6', 6)" class="id">length</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs5', 7)" onmouseover="showTip(event, 'fs5', 7)" class="vt">int</span><span class="pn">)</span> <span class="o">=</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs7', 8)" onmouseover="showTip(event, 'fs7', 8)" class="fn">swap</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs2', 9)" onmouseover="showTip(event, 'fs2', 9)" class="id">a</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs3', 10)" onmouseover="showTip(event, 'fs3', 10)" class="vt">float</span><span class="pn">[</span><span class="pn">]</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs8', 11)" onmouseover="showTip(event, 'fs8', 11)" class="id">i</span> <span onmouseout="hideTip(event, 'fs9', 12)" onmouseover="showTip(event, 'fs9', 12)" class="id">j</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs10', 13)" onmouseover="showTip(event, 'fs10', 13)" class="id">temp</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs2', 14)" onmouseover="showTip(event, 'fs2', 14)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs8', 15)" onmouseover="showTip(event, 'fs8', 15)" class="id">i</span><span class="pn">]</span>
<span onmouseout="hideTip(event, 'fs2', 16)" onmouseover="showTip(event, 'fs2', 16)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs8', 17)" onmouseover="showTip(event, 'fs8', 17)" class="id">i</span><span class="pn">]</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs2', 18)" onmouseover="showTip(event, 'fs2', 18)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs9', 19)" onmouseover="showTip(event, 'fs9', 19)" class="id">j</span><span class="pn">]</span>
<span onmouseout="hideTip(event, 'fs2', 20)" onmouseover="showTip(event, 'fs2', 20)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs9', 21)" onmouseover="showTip(event, 'fs9', 21)" class="id">j</span><span class="pn">]</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs10', 22)" onmouseover="showTip(event, 'fs10', 22)" class="id">temp</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs11', 23)" onmouseover="showTip(event, 'fs11', 23)" class="fn">swapIf</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs2', 24)" onmouseover="showTip(event, 'fs2', 24)" class="id">a</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs3', 25)" onmouseover="showTip(event, 'fs3', 25)" class="vt">float</span><span class="pn">[</span><span class="pn">]</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs8', 26)" onmouseover="showTip(event, 'fs8', 26)" class="id">i</span> <span onmouseout="hideTip(event, 'fs9', 27)" onmouseover="showTip(event, 'fs9', 27)" class="id">j</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs12', 28)" onmouseover="showTip(event, 'fs12', 28)" class="id">ai</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs2', 29)" onmouseover="showTip(event, 'fs2', 29)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs8', 30)" onmouseover="showTip(event, 'fs8', 30)" class="id">i</span><span class="pn">]</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs13', 31)" onmouseover="showTip(event, 'fs13', 31)" class="id">aj</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs2', 32)" onmouseover="showTip(event, 'fs2', 32)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs9', 33)" onmouseover="showTip(event, 'fs9', 33)" class="id">j</span><span class="pn">]</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs12', 34)" onmouseover="showTip(event, 'fs12', 34)" class="id">ai</span><span class="o">></span><span onmouseout="hideTip(event, 'fs13', 35)" onmouseover="showTip(event, 'fs13', 35)" class="id">aj</span> <span class="k">then</span>
<span onmouseout="hideTip(event, 'fs2', 36)" onmouseover="showTip(event, 'fs2', 36)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs8', 37)" onmouseover="showTip(event, 'fs8', 37)" class="id">i</span><span class="pn">]</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs13', 38)" onmouseover="showTip(event, 'fs13', 38)" class="id">aj</span>
<span onmouseout="hideTip(event, 'fs2', 39)" onmouseover="showTip(event, 'fs2', 39)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs9', 40)" onmouseover="showTip(event, 'fs9', 40)" class="id">j</span><span class="pn">]</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs12', 41)" onmouseover="showTip(event, 'fs12', 41)" class="id">ai</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs14', 42)" onmouseover="showTip(event, 'fs14', 42)" class="id">k</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs6', 43)" onmouseover="showTip(event, 'fs6', 43)" class="id">length</span> <span class="o">></span><span class="pn">></span><span class="pn">></span> <span class="n">1</span>
<span class="k">let</span> <span class="k">rec</span> <span onmouseout="hideTip(event, 'fs15', 44)" onmouseover="showTip(event, 'fs15', 44)" class="fn">outerLoop</span> <span onmouseout="hideTip(event, 'fs16', 45)" onmouseover="showTip(event, 'fs16', 45)" class="id">lo</span> <span onmouseout="hideTip(event, 'fs17', 46)" onmouseover="showTip(event, 'fs17', 46)" class="id">hi</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs11', 47)" onmouseover="showTip(event, 'fs11', 47)" class="fn">swapIf</span> <span onmouseout="hideTip(event, 'fs2', 48)" onmouseover="showTip(event, 'fs2', 48)" class="id">a</span> <span onmouseout="hideTip(event, 'fs16', 49)" onmouseover="showTip(event, 'fs16', 49)" class="id">lo</span> <span onmouseout="hideTip(event, 'fs14', 50)" onmouseover="showTip(event, 'fs14', 50)" class="id">k</span>
<span onmouseout="hideTip(event, 'fs11', 51)" onmouseover="showTip(event, 'fs11', 51)" class="fn">swapIf</span> <span onmouseout="hideTip(event, 'fs2', 52)" onmouseover="showTip(event, 'fs2', 52)" class="id">a</span> <span onmouseout="hideTip(event, 'fs16', 53)" onmouseover="showTip(event, 'fs16', 53)" class="id">lo</span> <span onmouseout="hideTip(event, 'fs17', 54)" onmouseover="showTip(event, 'fs17', 54)" class="id">hi</span>
<span onmouseout="hideTip(event, 'fs11', 55)" onmouseover="showTip(event, 'fs11', 55)" class="fn">swapIf</span> <span onmouseout="hideTip(event, 'fs2', 56)" onmouseover="showTip(event, 'fs2', 56)" class="id">a</span> <span onmouseout="hideTip(event, 'fs14', 57)" onmouseover="showTip(event, 'fs14', 57)" class="id">k</span> <span onmouseout="hideTip(event, 'fs17', 58)" onmouseover="showTip(event, 'fs17', 58)" class="id">hi</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs18', 59)" onmouseover="showTip(event, 'fs18', 59)" class="id">pivot</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs2', 60)" onmouseover="showTip(event, 'fs2', 60)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs14', 61)" onmouseover="showTip(event, 'fs14', 61)" class="id">k</span><span class="pn">]</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs19', 62)" onmouseover="showTip(event, 'fs19', 62)" class="id">resetLo</span> <span class="o">=</span> <span class="k">if</span> <span onmouseout="hideTip(event, 'fs2', 63)" onmouseover="showTip(event, 'fs2', 63)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs16', 64)" onmouseover="showTip(event, 'fs16', 64)" class="id">lo</span><span class="pn">]</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs18', 65)" onmouseover="showTip(event, 'fs18', 65)" class="id">pivot</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs2', 66)" onmouseover="showTip(event, 'fs2', 66)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs16', 67)" onmouseover="showTip(event, 'fs16', 67)" class="id">lo</span><span class="pn">]</span><span class="k"><-</span><span class="id">Double</span><span class="pn">.</span><span class="id">MinValue</span><span class="pn">;</span> <span class="k">true</span> <span class="k">else</span> <span class="k">false</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs20', 68)" onmouseover="showTip(event, 'fs20', 68)" class="id">resetHi</span> <span class="o">=</span> <span class="k">if</span> <span onmouseout="hideTip(event, 'fs2', 69)" onmouseover="showTip(event, 'fs2', 69)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs17', 70)" onmouseover="showTip(event, 'fs17', 70)" class="id">hi</span><span class="pn">]</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs18', 71)" onmouseover="showTip(event, 'fs18', 71)" class="id">pivot</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs2', 72)" onmouseover="showTip(event, 'fs2', 72)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs17', 73)" onmouseover="showTip(event, 'fs17', 73)" class="id">hi</span><span class="pn">]</span><span class="k"><-</span><span class="id">Double</span><span class="pn">.</span><span class="id">MaxValue</span><span class="pn">;</span> <span class="k">true</span> <span class="k">else</span> <span class="k">false</span>
<span class="k">let</span> <span class="k">mutable</span> <span onmouseout="hideTip(event, 'fs21', 74)" onmouseover="showTip(event, 'fs21', 74)" class="mv">i</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs16', 75)" onmouseover="showTip(event, 'fs16', 75)" class="id">lo</span><span class="o">+</span><span class="n">1</span>
<span class="k">let</span> <span class="k">mutable</span> <span onmouseout="hideTip(event, 'fs22', 76)" onmouseover="showTip(event, 'fs22', 76)" class="mv">j</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs17', 77)" onmouseover="showTip(event, 'fs17', 77)" class="id">hi</span><span class="o">-</span><span class="n">1</span>
<span class="k">while</span> <span onmouseout="hideTip(event, 'fs2', 78)" onmouseover="showTip(event, 'fs2', 78)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs21', 79)" onmouseover="showTip(event, 'fs21', 79)" class="mv">i</span><span class="pn">]</span><span class="o"><=</span><span onmouseout="hideTip(event, 'fs18', 80)" onmouseover="showTip(event, 'fs18', 80)" class="id">pivot</span> <span class="k">do</span> <span onmouseout="hideTip(event, 'fs21', 81)" onmouseover="showTip(event, 'fs21', 81)" class="mv">i</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs21', 82)" onmouseover="showTip(event, 'fs21', 82)" class="mv">i</span><span class="o">+</span><span class="n">1</span>
<span class="k">while</span> <span onmouseout="hideTip(event, 'fs2', 83)" onmouseover="showTip(event, 'fs2', 83)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs22', 84)" onmouseover="showTip(event, 'fs22', 84)" class="mv">j</span><span class="pn">]</span><span class="o">></span><span class="o">=</span><span onmouseout="hideTip(event, 'fs18', 85)" onmouseover="showTip(event, 'fs18', 85)" class="id">pivot</span> <span class="k">do</span> <span onmouseout="hideTip(event, 'fs22', 86)" onmouseover="showTip(event, 'fs22', 86)" class="mv">j</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs22', 87)" onmouseover="showTip(event, 'fs22', 87)" class="mv">j</span><span class="o">-</span><span class="n">1</span>
<span class="k">while</span> <span onmouseout="hideTip(event, 'fs21', 88)" onmouseover="showTip(event, 'fs21', 88)" class="mv">i</span><span class="o"><</span><span onmouseout="hideTip(event, 'fs14', 89)" onmouseover="showTip(event, 'fs14', 89)" class="id">k</span> <span class="o">&&</span> <span onmouseout="hideTip(event, 'fs14', 90)" onmouseover="showTip(event, 'fs14', 90)" class="id">k</span><span class="o"><</span><span onmouseout="hideTip(event, 'fs22', 91)" onmouseover="showTip(event, 'fs22', 91)" class="mv">j</span> <span class="k">do</span>
<span onmouseout="hideTip(event, 'fs7', 92)" onmouseover="showTip(event, 'fs7', 92)" class="fn">swap</span> <span onmouseout="hideTip(event, 'fs2', 93)" onmouseover="showTip(event, 'fs2', 93)" class="id">a</span> <span onmouseout="hideTip(event, 'fs21', 94)" onmouseover="showTip(event, 'fs21', 94)" class="mv">i</span> <span onmouseout="hideTip(event, 'fs22', 95)" onmouseover="showTip(event, 'fs22', 95)" class="mv">j</span>
<span onmouseout="hideTip(event, 'fs21', 96)" onmouseover="showTip(event, 'fs21', 96)" class="mv">i</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs21', 97)" onmouseover="showTip(event, 'fs21', 97)" class="mv">i</span><span class="o">+</span><span class="n">1</span>
<span onmouseout="hideTip(event, 'fs22', 98)" onmouseover="showTip(event, 'fs22', 98)" class="mv">j</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs22', 99)" onmouseover="showTip(event, 'fs22', 99)" class="mv">j</span><span class="o">-</span><span class="n">1</span>
<span class="k">while</span> <span onmouseout="hideTip(event, 'fs2', 100)" onmouseover="showTip(event, 'fs2', 100)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs21', 101)" onmouseover="showTip(event, 'fs21', 101)" class="mv">i</span><span class="pn">]</span><span class="o"><=</span><span onmouseout="hideTip(event, 'fs18', 102)" onmouseover="showTip(event, 'fs18', 102)" class="id">pivot</span> <span class="k">do</span> <span onmouseout="hideTip(event, 'fs21', 103)" onmouseover="showTip(event, 'fs21', 103)" class="mv">i</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs21', 104)" onmouseover="showTip(event, 'fs21', 104)" class="mv">i</span><span class="o">+</span><span class="n">1</span>
<span class="k">while</span> <span onmouseout="hideTip(event, 'fs2', 105)" onmouseover="showTip(event, 'fs2', 105)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs22', 106)" onmouseover="showTip(event, 'fs22', 106)" class="mv">j</span><span class="pn">]</span><span class="o">></span><span class="o">=</span><span onmouseout="hideTip(event, 'fs18', 107)" onmouseover="showTip(event, 'fs18', 107)" class="id">pivot</span> <span class="k">do</span> <span onmouseout="hideTip(event, 'fs22', 108)" onmouseover="showTip(event, 'fs22', 108)" class="mv">j</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs22', 109)" onmouseover="showTip(event, 'fs22', 109)" class="mv">j</span><span class="o">-</span><span class="n">1</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs21', 110)" onmouseover="showTip(event, 'fs21', 110)" class="mv">i</span><span class="o"><</span><span onmouseout="hideTip(event, 'fs22', 111)" onmouseover="showTip(event, 'fs22', 111)" class="mv">j</span> <span class="k">then</span>
<span onmouseout="hideTip(event, 'fs7', 112)" onmouseover="showTip(event, 'fs7', 112)" class="fn">swap</span> <span onmouseout="hideTip(event, 'fs2', 113)" onmouseover="showTip(event, 'fs2', 113)" class="id">a</span> <span onmouseout="hideTip(event, 'fs21', 114)" onmouseover="showTip(event, 'fs21', 114)" class="mv">i</span> <span onmouseout="hideTip(event, 'fs22', 115)" onmouseover="showTip(event, 'fs22', 115)" class="mv">j</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs14', 116)" onmouseover="showTip(event, 'fs14', 116)" class="id">k</span><span class="o"><</span><span onmouseout="hideTip(event, 'fs22', 117)" onmouseover="showTip(event, 'fs22', 117)" class="mv">j</span> <span class="k">then</span>
<span onmouseout="hideTip(event, 'fs21', 118)" onmouseover="showTip(event, 'fs21', 118)" class="mv">i</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs16', 119)" onmouseover="showTip(event, 'fs16', 119)" class="id">lo</span>
<span onmouseout="hideTip(event, 'fs22', 120)" onmouseover="showTip(event, 'fs22', 120)" class="mv">j</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs22', 121)" onmouseover="showTip(event, 'fs22', 121)" class="mv">j</span><span class="o">-</span><span class="n">1</span>
<span class="k">while</span> <span onmouseout="hideTip(event, 'fs2', 122)" onmouseover="showTip(event, 'fs2', 122)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs22', 123)" onmouseover="showTip(event, 'fs22', 123)" class="mv">j</span><span class="pn">]</span><span class="o">></span><span class="o">=</span><span onmouseout="hideTip(event, 'fs18', 124)" onmouseover="showTip(event, 'fs18', 124)" class="id">pivot</span> <span class="k">do</span> <span onmouseout="hideTip(event, 'fs22', 125)" onmouseover="showTip(event, 'fs22', 125)" class="mv">j</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs22', 126)" onmouseover="showTip(event, 'fs22', 126)" class="mv">j</span><span class="o">-</span><span class="n">1</span>
<span class="k">else</span>
<span onmouseout="hideTip(event, 'fs22', 127)" onmouseover="showTip(event, 'fs22', 127)" class="mv">j</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs17', 128)" onmouseover="showTip(event, 'fs17', 128)" class="id">hi</span>
<span onmouseout="hideTip(event, 'fs21', 129)" onmouseover="showTip(event, 'fs21', 129)" class="mv">i</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs21', 130)" onmouseover="showTip(event, 'fs21', 130)" class="mv">i</span><span class="o">+</span><span class="n">1</span>
<span class="k">while</span> <span onmouseout="hideTip(event, 'fs2', 131)" onmouseover="showTip(event, 'fs2', 131)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs21', 132)" onmouseover="showTip(event, 'fs21', 132)" class="mv">i</span><span class="pn">]</span><span class="o"><=</span><span onmouseout="hideTip(event, 'fs18', 133)" onmouseover="showTip(event, 'fs18', 133)" class="id">pivot</span> <span class="k">do</span> <span onmouseout="hideTip(event, 'fs21', 134)" onmouseover="showTip(event, 'fs21', 134)" class="mv">i</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs21', 135)" onmouseover="showTip(event, 'fs21', 135)" class="mv">i</span><span class="o">+</span><span class="n">1</span>
<span class="k">else</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs14', 136)" onmouseover="showTip(event, 'fs14', 136)" class="id">k</span><span class="o"><</span><span onmouseout="hideTip(event, 'fs22', 137)" onmouseover="showTip(event, 'fs22', 137)" class="mv">j</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs21', 138)" onmouseover="showTip(event, 'fs21', 138)" class="mv">i</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs16', 139)" onmouseover="showTip(event, 'fs16', 139)" class="id">lo</span>
<span class="k">elif</span> <span onmouseout="hideTip(event, 'fs14', 140)" onmouseover="showTip(event, 'fs14', 140)" class="id">k</span><span class="o">></span><span onmouseout="hideTip(event, 'fs21', 141)" onmouseover="showTip(event, 'fs21', 141)" class="mv">i</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs22', 142)" onmouseover="showTip(event, 'fs22', 142)" class="mv">j</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs17', 143)" onmouseover="showTip(event, 'fs17', 143)" class="id">hi</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs19', 144)" onmouseover="showTip(event, 'fs19', 144)" class="id">resetLo</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs2', 145)" onmouseover="showTip(event, 'fs2', 145)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs16', 146)" onmouseover="showTip(event, 'fs16', 146)" class="id">lo</span><span class="pn">]</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs18', 147)" onmouseover="showTip(event, 'fs18', 147)" class="id">pivot</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs20', 148)" onmouseover="showTip(event, 'fs20', 148)" class="id">resetHi</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs2', 149)" onmouseover="showTip(event, 'fs2', 149)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs17', 150)" onmouseover="showTip(event, 'fs17', 150)" class="id">hi</span><span class="pn">]</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs18', 151)" onmouseover="showTip(event, 'fs18', 151)" class="id">pivot</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs21', 152)" onmouseover="showTip(event, 'fs21', 152)" class="mv">i</span><span class="o">></span><span class="o">=</span><span onmouseout="hideTip(event, 'fs22', 153)" onmouseover="showTip(event, 'fs22', 153)" class="mv">j</span> <span class="k">then</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs6', 154)" onmouseover="showTip(event, 'fs6', 154)" class="id">length</span> <span class="o">&&&</span> <span class="n">1</span> <span class="o">=</span> <span class="n">0</span> <span class="o">&&</span> <span onmouseout="hideTip(event, 'fs22', 155)" onmouseover="showTip(event, 'fs22', 155)" class="mv">j</span><span class="o">+</span><span class="n">1</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs14', 156)" onmouseover="showTip(event, 'fs14', 156)" class="id">k</span> <span class="k">then</span>
<span class="k">let</span> <span class="k">mutable</span> <span onmouseout="hideTip(event, 'fs23', 157)" onmouseover="showTip(event, 'fs23', 157)" class="mv">v</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs2', 158)" onmouseover="showTip(event, 'fs2', 158)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs16', 159)" onmouseover="showTip(event, 'fs16', 159)" class="id">lo</span><span class="pn">]</span>
<span class="k">for</span> <span onmouseout="hideTip(event, 'fs8', 160)" onmouseover="showTip(event, 'fs8', 160)" class="id">i</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs16', 161)" onmouseover="showTip(event, 'fs16', 161)" class="id">lo</span><span class="o">+</span><span class="n">1</span> <span class="k">to</span> <span onmouseout="hideTip(event, 'fs22', 162)" onmouseover="showTip(event, 'fs22', 162)" class="mv">j</span> <span class="k">do</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs2', 163)" onmouseover="showTip(event, 'fs2', 163)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs8', 164)" onmouseover="showTip(event, 'fs8', 164)" class="id">i</span><span class="pn">]</span><span class="o">></span><span onmouseout="hideTip(event, 'fs23', 165)" onmouseover="showTip(event, 'fs23', 165)" class="mv">v</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs23', 166)" onmouseover="showTip(event, 'fs23', 166)" class="mv">v</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs2', 167)" onmouseover="showTip(event, 'fs2', 167)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs8', 168)" onmouseover="showTip(event, 'fs8', 168)" class="id">i</span><span class="pn">]</span>
<span class="pn">(</span><span onmouseout="hideTip(event, 'fs23', 169)" onmouseover="showTip(event, 'fs23', 169)" class="mv">v</span> <span class="o">+</span> <span onmouseout="hideTip(event, 'fs18', 170)" onmouseover="showTip(event, 'fs18', 170)" class="id">pivot</span><span class="pn">)</span> <span class="o">*</span> <span class="n">0.5</span>
<span class="k">else</span> <span onmouseout="hideTip(event, 'fs18', 171)" onmouseover="showTip(event, 'fs18', 171)" class="id">pivot</span>
<span class="k">else</span> <span onmouseout="hideTip(event, 'fs15', 172)" onmouseover="showTip(event, 'fs15', 172)" class="fn">outerLoop</span> <span onmouseout="hideTip(event, 'fs21', 173)" onmouseover="showTip(event, 'fs21', 173)" class="mv">i</span> <span onmouseout="hideTip(event, 'fs22', 174)" onmouseover="showTip(event, 'fs22', 174)" class="mv">j</span>
<span onmouseout="hideTip(event, 'fs15', 175)" onmouseover="showTip(event, 'fs15', 175)" class="fn">outerLoop</span> <span onmouseout="hideTip(event, 'fs4', 176)" onmouseover="showTip(event, 'fs4', 176)" class="id">index</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs4', 177)" onmouseover="showTip(event, 'fs4', 177)" class="id">index</span><span class="o">+</span><span onmouseout="hideTip(event, 'fs6', 178)" onmouseover="showTip(event, 'fs6', 178)" class="id">length</span><span class="o">-</span><span class="n">1</span><span class="pn">)</span>
</code></pre>
<h2><a name="Performance-Testing" class="anchor" href="#Performance-Testing">Performance Testing</a></h2>
<p>The algorithm is compared to a full sort, the <a href="https://numerics.mathdotnet.com/DescriptiveStatistics.html#Median">Math.Net</a> Quickselect and the <a href="https://github.com/AndreyAkinshin/perfolizer#quickselectadaptive">Perfolizer</a> QuickSelectAdaptive algorithms.</p>
<img src="/public/test/median_tests.png" title="median tests"/>
<p>This performance testing technique created as part of a new testing library <a href="/blog/2020/03/18/integrated-random-testing">prototype</a> uses a statistical test on the counts of the faster of two algorithms.
The Median and MAD are estimated as useful information.
As well as being robust to outliers they are also a good compliment since the faster algorithm will always have a positive Median performance improvement.
This may not be true of the Mean.</p>
<p>Performance testing can be run across all threads and also while running other tests.
It is also able to compare a range of input data instead of just a single data input.</p>
<img src="/public/test/median.png" title="median"/>
<p>It reaches statistically significate results extremely quickly compared to tests based on the Mean.
A sigma of 6 gives a good stopping criteria and is used when skipping completed tests.</p>
<h2><a name="Online-Estimator" class="anchor" href="#Online-Estimator">Online Estimator</a></h2>
<p>In a previous <a href="/blog/2016/05/20/performance-testing">post</a> online statistical calculations were discussed.
The following online Median and MAD estimator is a hybrid of an exact Median and MAD calculation followed by a recursive estimator.
The size of the exact sample <code>N</code> and a learning rate <code>Eta</code> are taken as a parameters.</p>
<p>The standard error of the sample Median approximates to:</p>
<p><span class="math">\[SE \sim \frac{MAD}{\sqrt{n}}\]</span></p>
<p>A fixed fraction <code>Eta</code> of the MAD for the learning rate is used as it provides a parameter with a sensible scale.</p>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="k">type</span> <span class="rt">MedianEstimator</span> <span class="o">=</span>
<span class="k">val</span> <span class="k">mutable</span> <span class="k">private</span> <span onmouseout="hideTip(event, 'fs24', 179)" onmouseover="showTip(event, 'fs24', 179)" class="mv">A</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs3', 180)" onmouseover="showTip(event, 'fs3', 180)" class="vt">float</span> <span onmouseout="hideTip(event, 'fs25', 181)" onmouseover="showTip(event, 'fs25', 181)" class="rt">array</span>
<span class="k">val</span> <span class="k">mutable</span> <span class="k">private</span> <span onmouseout="hideTip(event, 'fs26', 182)" onmouseover="showTip(event, 'fs26', 182)" class="mv">Median</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs3', 183)" onmouseover="showTip(event, 'fs3', 183)" class="vt">float</span>
<span class="k">val</span> <span class="k">mutable</span> <span class="k">private</span> <span onmouseout="hideTip(event, 'fs27', 184)" onmouseover="showTip(event, 'fs27', 184)" class="mv">MAD</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs3', 185)" onmouseover="showTip(event, 'fs3', 185)" class="vt">float</span>
<span class="k">val</span> <span class="k">private</span> <span onmouseout="hideTip(event, 'fs28', 186)" onmouseover="showTip(event, 'fs28', 186)" class="id">Eta</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs3', 187)" onmouseover="showTip(event, 'fs3', 187)" class="vt">float</span>
<span class="k">val</span> <span class="k">mutable</span> <span onmouseout="hideTip(event, 'fs29', 188)" onmouseover="showTip(event, 'fs29', 188)" class="mv">N</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs5', 189)" onmouseover="showTip(event, 'fs5', 189)" class="vt">int</span>
<span class="k">new</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs30', 190)" onmouseover="showTip(event, 'fs30', 190)" class="id">n</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs5', 191)" onmouseover="showTip(event, 'fs5', 191)" class="vt">int</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs31', 192)" onmouseover="showTip(event, 'fs31', 192)" class="id">eta</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs3', 193)" onmouseover="showTip(event, 'fs3', 193)" class="vt">float</span><span class="pn">)</span> <span class="o">=</span>
<span class="pn">{</span><span onmouseout="hideTip(event, 'fs24', 194)" onmouseover="showTip(event, 'fs24', 194)" class="mv">A</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs32', 195)" onmouseover="showTip(event, 'fs32', 195)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs33', 196)" onmouseover="showTip(event, 'fs33', 196)" class="id">zeroCreate</span> <span onmouseout="hideTip(event, 'fs30', 197)" onmouseover="showTip(event, 'fs30', 197)" class="id">n</span><span class="pn">;</span> <span class="mv">Median</span> <span class="o">=</span> <span class="n">0.0</span><span class="pn">;</span> <span class="mv">MAD</span> <span class="o">=</span> <span class="n">0.0</span><span class="pn">;</span> <span class="id">Eta</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs31', 198)" onmouseover="showTip(event, 'fs31', 198)" class="id">eta</span><span class="pn">;</span> <span onmouseout="hideTip(event, 'fs29', 199)" onmouseover="showTip(event, 'fs29', 199)" class="mv">N</span> <span class="o">=</span> <span class="n">0</span><span class="pn">}</span>
<span class="k">member</span> <span onmouseout="hideTip(event, 'fs34', 200)" onmouseover="showTip(event, 'fs34', 200)" class="id">m</span><span class="pn">.</span><span class="prop">MedianAndMAD</span> <span class="o">=</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs35', 201)" onmouseover="showTip(event, 'fs35', 201)" class="fn">isNull</span> <span onmouseout="hideTip(event, 'fs34', 202)" onmouseover="showTip(event, 'fs34', 202)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs24', 203)" onmouseover="showTip(event, 'fs24', 203)" class="id">A</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs34', 204)" onmouseover="showTip(event, 'fs34', 204)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs26', 205)" onmouseover="showTip(event, 'fs26', 205)" class="id">Median</span><span class="pn">,</span> <span onmouseout="hideTip(event, 'fs34', 206)" onmouseover="showTip(event, 'fs34', 206)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 207)" onmouseover="showTip(event, 'fs27', 207)" class="id">MAD</span>
<span class="k">else</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs36', 208)" onmouseover="showTip(event, 'fs36', 208)" class="id">median</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs37', 209)" onmouseover="showTip(event, 'fs37', 209)" class="m">Statistics</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs1', 210)" onmouseover="showTip(event, 'fs1', 210)" class="id">medianInPlace</span> <span onmouseout="hideTip(event, 'fs34', 211)" onmouseover="showTip(event, 'fs34', 211)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs24', 212)" onmouseover="showTip(event, 'fs24', 212)" class="id">A</span> <span class="n">0</span> <span onmouseout="hideTip(event, 'fs34', 213)" onmouseover="showTip(event, 'fs34', 213)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs29', 214)" onmouseover="showTip(event, 'fs29', 214)" class="id">N</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs38', 215)" onmouseover="showTip(event, 'fs38', 215)" class="id">mad</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs32', 216)" onmouseover="showTip(event, 'fs32', 216)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs39', 217)" onmouseover="showTip(event, 'fs39', 217)" class="id">sub</span> <span onmouseout="hideTip(event, 'fs34', 218)" onmouseover="showTip(event, 'fs34', 218)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs24', 219)" onmouseover="showTip(event, 'fs24', 219)" class="id">A</span> <span class="n">0</span> <span onmouseout="hideTip(event, 'fs34', 220)" onmouseover="showTip(event, 'fs34', 220)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs29', 221)" onmouseover="showTip(event, 'fs29', 221)" class="id">N</span>
<span class="k">for</span> <span onmouseout="hideTip(event, 'fs8', 222)" onmouseover="showTip(event, 'fs8', 222)" class="id">i</span> <span class="o">=</span> <span class="n">0</span> <span class="k">to</span> <span onmouseout="hideTip(event, 'fs38', 223)" onmouseover="showTip(event, 'fs38', 223)" class="id">mad</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs40', 224)" onmouseover="showTip(event, 'fs40', 224)" class="id">Length</span><span class="o">-</span><span class="n">1</span> <span class="k">do</span>
<span onmouseout="hideTip(event, 'fs38', 225)" onmouseover="showTip(event, 'fs38', 225)" class="id">mad</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs8', 226)" onmouseover="showTip(event, 'fs8', 226)" class="id">i</span><span class="pn">]</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs41', 227)" onmouseover="showTip(event, 'fs41', 227)" class="fn">abs</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs38', 228)" onmouseover="showTip(event, 'fs38', 228)" class="id">mad</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs8', 229)" onmouseover="showTip(event, 'fs8', 229)" class="id">i</span><span class="pn">]</span><span class="o">-</span><span onmouseout="hideTip(event, 'fs36', 230)" onmouseover="showTip(event, 'fs36', 230)" class="id">median</span><span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs36', 231)" onmouseover="showTip(event, 'fs36', 231)" class="id">median</span><span class="pn">,</span> <span onmouseout="hideTip(event, 'fs37', 232)" onmouseover="showTip(event, 'fs37', 232)" class="m">Statistics</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs1', 233)" onmouseover="showTip(event, 'fs1', 233)" class="id">medianInPlace</span> <span onmouseout="hideTip(event, 'fs38', 234)" onmouseover="showTip(event, 'fs38', 234)" class="id">mad</span> <span class="n">0</span> <span onmouseout="hideTip(event, 'fs38', 235)" onmouseover="showTip(event, 'fs38', 235)" class="id">mad</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs40', 236)" onmouseover="showTip(event, 'fs40', 236)" class="id">Length</span>
<span class="k">member</span> <span onmouseout="hideTip(event, 'fs34', 237)" onmouseover="showTip(event, 'fs34', 237)" class="id">m</span><span class="pn">.</span><span class="fn">Add</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs42', 238)" onmouseover="showTip(event, 'fs42', 238)" class="id">s</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs3', 239)" onmouseover="showTip(event, 'fs3', 239)" class="vt">float</span><span class="pn">)</span> <span class="o">=</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs35', 240)" onmouseover="showTip(event, 'fs35', 240)" class="fn">isNull</span> <span onmouseout="hideTip(event, 'fs34', 241)" onmouseover="showTip(event, 'fs34', 241)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs24', 242)" onmouseover="showTip(event, 'fs24', 242)" class="id">A</span> <span class="k">then</span>
<span onmouseout="hideTip(event, 'fs34', 243)" onmouseover="showTip(event, 'fs34', 243)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs26', 244)" onmouseover="showTip(event, 'fs26', 244)" class="id">Median</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs34', 245)" onmouseover="showTip(event, 'fs34', 245)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs26', 246)" onmouseover="showTip(event, 'fs26', 246)" class="id">Median</span> <span class="o">+</span> <span onmouseout="hideTip(event, 'fs34', 247)" onmouseover="showTip(event, 'fs34', 247)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 248)" onmouseover="showTip(event, 'fs27', 248)" class="id">MAD</span> <span class="o">*</span> <span onmouseout="hideTip(event, 'fs34', 249)" onmouseover="showTip(event, 'fs34', 249)" class="id">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs28', 250)" onmouseover="showTip(event, 'fs28', 250)" class="id">Eta</span> <span class="o">*</span> <span onmouseout="hideTip(event, 'fs3', 251)" onmouseover="showTip(event, 'fs3', 251)" class="fn">float</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs43', 252)" onmouseover="showTip(event, 'fs43', 252)" class="fn">sign</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs42', 253)" onmouseover="showTip(event, 'fs42', 253)" class="id">s</span><span class="o">-</span><span onmouseout="hideTip(event, 'fs34', 254)" onmouseover="showTip(event, 'fs34', 254)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs26', 255)" onmouseover="showTip(event, 'fs26', 255)" class="id">Median</span><span class="pn">)</span><span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs34', 256)" onmouseover="showTip(event, 'fs34', 256)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 257)" onmouseover="showTip(event, 'fs27', 257)" class="id">MAD</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs34', 258)" onmouseover="showTip(event, 'fs34', 258)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 259)" onmouseover="showTip(event, 'fs27', 259)" class="id">MAD</span> <span class="o">+</span> <span onmouseout="hideTip(event, 'fs34', 260)" onmouseover="showTip(event, 'fs34', 260)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 261)" onmouseover="showTip(event, 'fs27', 261)" class="id">MAD</span> <span class="o">*</span> <span onmouseout="hideTip(event, 'fs34', 262)" onmouseover="showTip(event, 'fs34', 262)" class="id">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs28', 263)" onmouseover="showTip(event, 'fs28', 263)" class="id">Eta</span> <span class="o">*</span> <span onmouseout="hideTip(event, 'fs3', 264)" onmouseover="showTip(event, 'fs3', 264)" class="fn">float</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs43', 265)" onmouseover="showTip(event, 'fs43', 265)" class="fn">sign</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs41', 266)" onmouseover="showTip(event, 'fs41', 266)" class="fn">abs</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs42', 267)" onmouseover="showTip(event, 'fs42', 267)" class="id">s</span><span class="o">-</span><span onmouseout="hideTip(event, 'fs34', 268)" onmouseover="showTip(event, 'fs34', 268)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs26', 269)" onmouseover="showTip(event, 'fs26', 269)" class="id">Median</span><span class="pn">)</span><span class="o">-</span><span onmouseout="hideTip(event, 'fs34', 270)" onmouseover="showTip(event, 'fs34', 270)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 271)" onmouseover="showTip(event, 'fs27', 271)" class="id">MAD</span><span class="pn">)</span><span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs34', 272)" onmouseover="showTip(event, 'fs34', 272)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs29', 273)" onmouseover="showTip(event, 'fs29', 273)" class="id">N</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs34', 274)" onmouseover="showTip(event, 'fs34', 274)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs29', 275)" onmouseover="showTip(event, 'fs29', 275)" class="id">N</span> <span class="o">+</span> <span class="n">1</span>
<span class="k">else</span>
<span onmouseout="hideTip(event, 'fs34', 276)" onmouseover="showTip(event, 'fs34', 276)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs24', 277)" onmouseover="showTip(event, 'fs24', 277)" class="id">A</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs34', 278)" onmouseover="showTip(event, 'fs34', 278)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs29', 279)" onmouseover="showTip(event, 'fs29', 279)" class="id">N</span><span class="pn">]</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs42', 280)" onmouseover="showTip(event, 'fs42', 280)" class="id">s</span>
<span onmouseout="hideTip(event, 'fs34', 281)" onmouseover="showTip(event, 'fs34', 281)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs29', 282)" onmouseover="showTip(event, 'fs29', 282)" class="id">N</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs34', 283)" onmouseover="showTip(event, 'fs34', 283)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs29', 284)" onmouseover="showTip(event, 'fs29', 284)" class="id">N</span> <span class="o">+</span> <span class="n">1</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs34', 285)" onmouseover="showTip(event, 'fs34', 285)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs29', 286)" onmouseover="showTip(event, 'fs29', 286)" class="id">N</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs34', 287)" onmouseover="showTip(event, 'fs34', 287)" class="id">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs24', 288)" onmouseover="showTip(event, 'fs24', 288)" class="mv">A</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs40', 289)" onmouseover="showTip(event, 'fs40', 289)" class="id">Length</span> <span class="k">then</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs36', 290)" onmouseover="showTip(event, 'fs36', 290)" class="id">median</span><span class="pn">,</span> <span onmouseout="hideTip(event, 'fs44', 291)" onmouseover="showTip(event, 'fs44', 291)" class="id">mad</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs34', 292)" onmouseover="showTip(event, 'fs34', 292)" class="id">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs45', 293)" onmouseover="showTip(event, 'fs45', 293)" class="id">MedianAndMAD</span>
<span onmouseout="hideTip(event, 'fs34', 294)" onmouseover="showTip(event, 'fs34', 294)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs26', 295)" onmouseover="showTip(event, 'fs26', 295)" class="id">Median</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs36', 296)" onmouseover="showTip(event, 'fs36', 296)" class="id">median</span>
<span onmouseout="hideTip(event, 'fs34', 297)" onmouseover="showTip(event, 'fs34', 297)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 298)" onmouseover="showTip(event, 'fs27', 298)" class="id">MAD</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs44', 299)" onmouseover="showTip(event, 'fs44', 299)" class="id">mad</span>
<span onmouseout="hideTip(event, 'fs34', 300)" onmouseover="showTip(event, 'fs34', 300)" class="mv">m</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs24', 301)" onmouseover="showTip(event, 'fs24', 301)" class="id">A</span> <span class="k"><-</span> <span class="k">null</span>
</code></pre>
<p>For performance testing <code>N=99</code> and <code>F=0.001</code> give stable results.</p>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p>The superior performance of the <a href="http://zabrodskyvlada.byethost10.com/aat/a_modi.html">MODIFIND</a> algorithm has again been demonstrated compared to current algorithms.</p>
<p>A new type of statistical performance testing has been introduced with useful features of being able to run in parallel and across a range of inputs efficiently.</p>
<p>A fixed memory online Median and MAD estimator has been created to provide efficient and consistent statistics for use in performance testing and other performance critical applications.</p>
<p>Although early days these techniques together look to provide robust and efficient results.</p>
<div class="tip" id="fs1">val medianInPlace : a:float [] -> index:int -> length:int -> float</div>
<div class="tip" id="fs2">val a : float []</div>
<div class="tip" id="fs3">Multiple items<br />val float : value:'T -> float (requires member op_Explicit)<br /><br />--------------------<br />type float = System.Double<br /><br />--------------------<br />type float<'Measure> = float</div>
<div class="tip" id="fs4">val index : int</div>
<div class="tip" id="fs5">Multiple items<br />val int : value:'T -> int (requires member op_Explicit)<br /><br />--------------------<br />type int = int32<br /><br />--------------------<br />type int<'Measure> = int</div>
<div class="tip" id="fs6">val length : int</div>
<div class="tip" id="fs7">val swap : (float [] -> int -> int -> unit)</div>
<div class="tip" id="fs8">val i : int</div>
<div class="tip" id="fs9">val j : int</div>
<div class="tip" id="fs10">val temp : float</div>
<div class="tip" id="fs11">val swapIf : (float [] -> int -> int -> unit)</div>
<div class="tip" id="fs12">val ai : float</div>
<div class="tip" id="fs13">val aj : float</div>
<div class="tip" id="fs14">val k : int</div>
<div class="tip" id="fs15">val outerLoop : (int -> int -> float)</div>
<div class="tip" id="fs16">val lo : int</div>
<div class="tip" id="fs17">val hi : int</div>
<div class="tip" id="fs18">val pivot : float</div>
<div class="tip" id="fs19">val resetLo : bool</div>
<div class="tip" id="fs20">val resetHi : bool</div>
<div class="tip" id="fs21">val mutable i : int</div>
<div class="tip" id="fs22">val mutable j : int</div>
<div class="tip" id="fs23">val mutable v : float</div>
<div class="tip" id="fs24">MedianEstimator.A: float array</div>
<div class="tip" id="fs25">type 'T array = 'T []</div>
<div class="tip" id="fs26">MedianEstimator.Median: float</div>
<div class="tip" id="fs27">MedianEstimator.MAD: float</div>
<div class="tip" id="fs28">MedianEstimator.Eta: float</div>
<div class="tip" id="fs29">MedianEstimator.N: int</div>
<div class="tip" id="fs30">val n : int</div>
<div class="tip" id="fs31">val eta : float</div>
<div class="tip" id="fs32">module Array<br /><br />from Microsoft.FSharp.Collections</div>
<div class="tip" id="fs33">val zeroCreate : count:int -> 'T []</div>
<div class="tip" id="fs34">val m : MedianEstimator</div>
<div class="tip" id="fs35">val isNull : value:'T -> bool (requires 'T : null)</div>
<div class="tip" id="fs36">val median : float</div>
<div class="tip" id="fs37">module Statistics<br /><br />from 2020-04-07-Median-Revisited</div>
<div class="tip" id="fs38">val mad : float []</div>
<div class="tip" id="fs39">val sub : array:'T [] -> startIndex:int -> count:int -> 'T []</div>
<div class="tip" id="fs40">property System.Array.Length: int</div>
<div class="tip" id="fs41">val abs : value:'T -> 'T (requires member Abs)</div>
<div class="tip" id="fs42">val s : float</div>
<div class="tip" id="fs43">val sign : value:'T -> int (requires member get_Sign)</div>
<div class="tip" id="fs44">val mad : float</div>
<div class="tip" id="fs45">property MedianEstimator.MedianAndMAD: float * float</div>
Integrated Random Testing
2020-03-18T00:00:00+00:00
http://anthonylloyd.github.io/blog/2020/03/18/integrated-random-testing
<p>For a while I've been annoyed by the performance of testing libraries in general, but also with how random testing and performance testing are not better integrated and multithreaded.
Testing libraries like <a href="https://github.com/haf/expecto">Expecto</a> do a great job of improving performance by running unit tests in parallel while also opening up useful functionality like stress testing.
I want to take this further with a new prototype.</p>
<p>The goal is a simpler, more lightweight testing library with faster, more integrated parallel random testing with automatic parallel shrinking.</p>
<p>The library aims to encourage the shift from a number of unit and regression tests with hard coded input and output data to fewer more general random tests.
This idea is covered well by John Hughes in <a href="https://youtu.be/DZhbmv8WsYU">Don't write tests!</a> and the idea of <a href="https://youtu.be/NcJOiQlzlXQ">One test to rule them all</a>.
Key takeaways are general random tests can provide more coverage for less test code, and larger test cases have a higher probability of finding a failure for a given execution time.</p>
<h2><a name="Features" class="anchor" href="#Features">Features</a></h2>
<ul>
<li>Asserts are no longer exception based and all are evaluated - More than one per test is encouraged. Simpler setup and faster for multi part testing.</li>
</ul>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="id">test</span> <span class="s">"PCG demo 1"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">pcg</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs178', 1555)" onmouseover="showTip(event, 'fs178', 1555)" class="id">PCG</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs216', 1556)" onmouseover="showTip(event, 'fs216', 1556)" class="id">TryParse</span> <span class="s">"36185706b82c2e03f8"</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs254', 1557)" onmouseover="showTip(event, 'fs254', 1557)" class="id">Option</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs295', 1558)" onmouseover="showTip(event, 'fs295', 1558)" class="id">get</span>
<span class="id">Test</span><span class="pn">.</span><span class="id">equal</span> <span class="id">pcg</span><span class="pn">.</span><span class="id">Stream</span> <span class="n">54</span> <span class="s">"stream"</span>
<span class="id">Test</span><span class="pn">.</span><span class="id">equal</span> <span class="id">pcg</span><span class="pn">.</span><span class="id">State</span> <span class="n">0x185706b82c2e03f8UL</span> <span class="s">"state"</span>
<span class="k">let</span> <span class="id">expectedNext</span> <span class="o">=</span> <span class="pn">[|</span><span class="n">0xa15c02b7u</span><span class="pn">;</span><span class="n">0x7b47f409u</span><span class="pn">|]</span> <span class="c">// ...</span>
<span class="k">let</span> <span class="id">expectedState</span> <span class="o">=</span> <span class="pn">[|</span><span class="n">0x2b47fed88766bb05UL</span><span class="pn">;</span><span class="n">0x8b33296d19bf5b4eUL</span><span class="pn">|]</span> <span class="c">// ...</span>
<span class="k">for</span> <span class="id">i</span> <span class="o">=</span> <span class="n">0</span> <span class="k">to</span> <span class="id">expectedNext</span><span class="pn">.</span><span class="id">Length</span><span class="o">-</span><span class="n">1</span> <span class="k">do</span>
<span class="id">Test</span><span class="pn">.</span><span class="id">equal</span> <span class="pn">(</span><span class="id">pcg</span><span class="pn">.</span><span class="id">Next</span><span class="pn">(</span><span class="pn">)</span><span class="pn">)</span> <span class="id">expectedNext</span><span class="pn">.</span><span class="pn">[</span><span class="id">i</span><span class="pn">]</span> <span class="pn">(</span><span class="s">"n"</span><span class="o">+</span><span onmouseout="hideTip(event, 'fs26', 1559)" onmouseover="showTip(event, 'fs26', 1559)" class="id">string</span> <span class="id">i</span><span class="pn">)</span>
<span class="id">Test</span><span class="pn">.</span><span class="id">equal</span> <span class="id">pcg</span><span class="pn">.</span><span class="id">State</span> <span class="id">expectedState</span><span class="pn">.</span><span class="pn">[</span><span class="id">i</span><span class="pn">]</span> <span class="pn">(</span><span class="s">"s"</span><span class="o">+</span><span onmouseout="hideTip(event, 'fs26', 1560)" onmouseover="showTip(event, 'fs26', 1560)" class="id">string</span> <span class="id">i</span><span class="pn">)</span>
<span class="pn">}</span>
</code></pre>
<ul>
<li>Integrated random testing - Simpler syntax. Easier to move to more general random testing. Run in fine grained parallel.</li>
</ul>
<pre class="fssnip highlighted"><code lang="fsharp"> <span class="id">test</span> <span class="s">"gen"</span> <span class="pn">{</span>
<span class="id">test</span> <span class="s">"int"</span> <span class="pn">{</span>
<span class="k">let!</span> <span class="id">s</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs184', 1561)" onmouseover="showTip(event, 'fs184', 1561)" class="id">Gen</span><span class="pn">.</span><span class="id">int</span>
<span class="k">let!</span> <span class="id">l</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs184', 1562)" onmouseover="showTip(event, 'fs184', 1562)" class="id">Gen</span><span class="pn">.</span><span class="id">int</span><span class="pn">.</span><span class="pn">[</span><span class="n">0..</span><span onmouseout="hideTip(event, 'fs33', 1563)" onmouseover="showTip(event, 'fs33', 1563)" class="id">Int32</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs275', 1564)" onmouseover="showTip(event, 'fs275', 1564)" class="id">MaxValue</span><span class="o">-</span><span class="id">s</span><span class="pn">]</span>
<span class="k">let!</span> <span class="id">i</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs184', 1565)" onmouseover="showTip(event, 'fs184', 1565)" class="id">Gen</span><span class="pn">.</span><span class="id">int</span><span class="pn">.</span><span class="pn">[</span><span class="id">s</span><span class="o">..</span><span class="id">s</span><span class="o">+</span><span class="id">l</span><span class="pn">]</span>
<span class="id">Test</span><span class="pn">.</span><span class="id">between</span> <span class="id">i</span> <span class="id">s</span> <span class="pn">(</span><span class="id">s</span><span class="o">+</span><span class="id">l</span><span class="pn">)</span> <span class="s">"between"</span>
<span class="pn">}</span>
<span class="id">test</span> <span class="s">"int distribution"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">freq</span> <span class="o">=</span> <span class="n">10</span>
<span class="k">let</span> <span class="id">buckets</span> <span class="o">=</span> <span class="n">1000</span>
<span class="k">let!</span> <span class="id">ints</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs184', 1566)" onmouseover="showTip(event, 'fs184', 1566)" class="id">Gen</span><span class="pn">.</span><span class="id">seq</span><span class="pn">.</span><span class="pn">[</span><span class="id">freq</span> <span class="pn">*</span> <span class="id">buckets</span><span class="pn">]</span> <span onmouseout="hideTip(event, 'fs184', 1567)" onmouseover="showTip(event, 'fs184', 1567)" class="id">Gen</span><span class="pn">.</span><span class="id">int</span><span class="pn">.</span><span class="pn">[</span><span class="n">0..</span><span class="id">buckets</span><span class="o">-</span><span class="n">1</span><span class="pn">]</span>
<span class="k">let</span> <span class="id">actual</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs105', 1568)" onmouseover="showTip(event, 'fs105', 1568)" class="id">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs108', 1569)" onmouseover="showTip(event, 'fs108', 1569)" class="id">zeroCreate</span> <span class="id">buckets</span>
<span onmouseout="hideTip(event, 'fs221', 1570)" onmouseover="showTip(event, 'fs221', 1570)" class="id">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs225', 1571)" onmouseover="showTip(event, 'fs225', 1571)" class="id">iter</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">i</span> <span class="k">-></span> <span class="id">actual</span><span class="pn">.</span><span class="pn">[</span><span class="id">i</span><span class="pn">]</span> <span class="k"><-</span> <span class="id">actual</span><span class="pn">.</span><span class="pn">[</span><span class="id">i</span><span class="pn">]</span> <span class="o">+</span> <span class="n">1</span><span class="pn">)</span> <span class="id">ints</span>
<span class="k">let</span> <span class="id">expected</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs105', 1572)" onmouseover="showTip(event, 'fs105', 1572)" class="id">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs296', 1573)" onmouseover="showTip(event, 'fs296', 1573)" class="id">create</span> <span class="id">buckets</span> <span class="id">freq</span>
<span class="id">Test</span><span class="pn">.</span><span class="id">chiSquared</span> <span class="id">actual</span> <span class="id">expected</span> <span class="s">"chi-squared"</span>
<span class="pn">}</span>
</code></pre>
<ul>
<li>No sizing or number of runs for random tests - Instead use distributions. More realistic large test cases.</li>
</ul>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="id">test</span> <span class="s">"list rev does nothing not"</span> <span class="pn">{</span>
<span class="k">let!</span> <span onmouseout="hideTip(event, 'fs165', 1574)" onmouseover="showTip(event, 'fs165', 1574)" class="id">list</span> <span class="o">=</span>
<span class="k">let</span> <span class="id">f</span> <span class="id">ma</span> <span class="id">mi</span> <span class="id">bu</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs297', 1575)" onmouseover="showTip(event, 'fs297', 1575)" class="id">Version</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs18', 1576)" onmouseover="showTip(event, 'fs18', 1576)" class="id">int</span> <span class="id">ma</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs18', 1577)" onmouseover="showTip(event, 'fs18', 1577)" class="id">int</span> <span class="id">mi</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs18', 1578)" onmouseover="showTip(event, 'fs18', 1578)" class="id">int</span> <span class="id">bu</span><span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs184', 1579)" onmouseover="showTip(event, 'fs184', 1579)" class="id">Gen</span><span class="pn">.</span><span class="id">map3</span> <span class="id">f</span> <span onmouseout="hideTip(event, 'fs184', 1580)" onmouseover="showTip(event, 'fs184', 1580)" class="id">Gen</span><span class="pn">.</span><span class="id">byte</span> <span onmouseout="hideTip(event, 'fs184', 1581)" onmouseover="showTip(event, 'fs184', 1581)" class="id">Gen</span><span class="pn">.</span><span class="id">byte</span> <span onmouseout="hideTip(event, 'fs184', 1582)" onmouseover="showTip(event, 'fs184', 1582)" class="id">Gen</span><span class="pn">.</span><span class="id">byte</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs184', 1583)" onmouseover="showTip(event, 'fs184', 1583)" class="id">Gen</span><span class="pn">.</span><span class="id">list</span><span class="pn">.</span><span class="pn">[</span><span class="n">0..</span><span class="n">100</span><span class="pn">]</span>
<span class="id">Test</span><span class="pn">.</span><span class="id">equal</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs64', 1584)" onmouseover="showTip(event, 'fs64', 1584)" class="id">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs209', 1585)" onmouseover="showTip(event, 'fs209', 1585)" class="id">rev</span> <span onmouseout="hideTip(event, 'fs165', 1586)" onmouseover="showTip(event, 'fs165', 1586)" class="id">list</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs165', 1587)" onmouseover="showTip(event, 'fs165', 1587)" class="id">list</span> <span class="s">"rev equal"</span>
<span class="pn">}</span>
</code></pre>
<ul>
<li>
Automatic parallel random shrinking giving a reproducible seed - Smaller candidates found using a fast <a href="https://www.pcg-random.org/">PCG</a> loop. Simpler reproducible examples.
<img src="/public/test/shrink.png" title="shrink" style="margin-left:-30px;margin-top:20px" />
</li>
<li>Stress testing in parallel across unit and random tests using <a href="https://www.pcg-random.org/">PCG</a> streams - Low sync, high performance, fine grained parallel testing.</li>
</ul>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="id">test</span> <span class="s">"multithreading"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">n</span> <span class="o">=</span> <span class="n">10</span>
<span class="k">let</span> <span class="id">ms</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs197', 1588)" onmouseover="showTip(event, 'fs197', 1588)" class="id">MapSlim</span><span class="pn">(</span><span class="pn">)</span>
<span class="id">test</span> <span class="s">"update"</span> <span class="pn">{</span>
<span class="k">let!</span> <span class="id">i</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs184', 1589)" onmouseover="showTip(event, 'fs184', 1589)" class="id">Gen</span><span class="pn">.</span><span class="id">int</span><span class="pn">.</span><span class="pn">[</span><span class="o">..</span><span class="id">n</span><span class="o">-</span><span class="n">1</span><span class="pn">]</span>
<span class="k">let</span> <span class="id">v</span> <span class="o">=</span> <span class="o">&</span><span class="id">ms</span><span class="pn">.</span><span class="id">GetRef</span> <span class="id">i</span>
<span class="id">v</span> <span class="k"><-</span> <span class="n">1</span><span class="o">-</span><span class="id">v</span>
<span class="pn">}</span>
<span class="id">test</span> <span class="s">"get"</span> <span class="pn">{</span>
<span class="k">let!</span> <span class="id">i</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs184', 1590)" onmouseover="showTip(event, 'fs184', 1590)" class="id">Gen</span><span class="pn">.</span><span class="id">int</span><span class="pn">.</span><span class="pn">[</span><span class="o">..</span><span class="id">n</span><span class="o">-</span><span class="n">1</span><span class="pn">]</span>
<span class="k">let</span> <span class="id">v</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs298', 1591)" onmouseover="showTip(event, 'fs298', 1591)" class="id">defaultValueArg</span> <span class="pn">(</span><span class="id">ms</span><span class="pn">.</span><span class="id">GetOption</span> <span class="id">i</span><span class="pn">)</span> <span class="n">0</span>
<span class="id">Test</span><span class="pn">.</span><span class="id">isTrue</span> <span class="pn">(</span><span class="id">v</span><span class="o">=</span><span class="n">0</span> <span class="o">||</span> <span class="id">v</span><span class="o">=</span><span class="n">1</span><span class="pn">)</span> <span class="s">"get is 0 or 1"</span>
<span class="pn">}</span>
<span class="id">test</span> <span class="s">"item"</span> <span class="pn">{</span>
<span class="k">if</span> <span class="id">ms</span><span class="pn">.</span><span class="id">Count</span> <span class="pn">></span> <span class="n">0</span> <span class="k">then</span>
<span class="k">let!</span> <span class="id">i</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs184', 1592)" onmouseover="showTip(event, 'fs184', 1592)" class="id">Gen</span><span class="pn">.</span><span class="id">int</span><span class="pn">.</span><span class="pn">[</span><span class="o">..</span><span class="id">ms</span><span class="pn">.</span><span class="id">Count</span><span class="o">-</span><span class="n">1</span><span class="pn">]</span>
<span class="k">let</span> <span class="id">k</span><span class="pn">,</span><span class="id">v</span> <span class="o">=</span> <span class="id">ms</span><span class="pn">.</span><span class="id">Item</span> <span class="id">i</span>
<span class="id">Test</span><span class="pn">.</span><span class="id">lessThan</span> <span class="id">k</span> <span class="id">n</span> <span class="s">"key is ok"</span>
<span class="id">Test</span><span class="pn">.</span><span class="id">isTrue</span> <span class="pn">(</span><span class="id">v</span><span class="o">=</span><span class="n">0</span> <span class="o">||</span> <span class="id">v</span><span class="o">=</span><span class="n">1</span><span class="pn">)</span> <span class="s">"value is 0 or 1"</span>
<span class="pn">}</span>
<span class="pn">}</span>
</code></pre>
<ul>
<li>Integrated performance testing - Performance tests can be random and run in parallel with statistics collected across all threads.</li>
</ul>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="id">test</span> <span class="s">"mapslim"</span> <span class="pn">{</span>
<span class="id">test</span> <span class="s">"performance"</span> <span class="pn">{</span>
<span class="id">test</span> <span class="s">"get"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">ms</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs197', 1593)" onmouseover="showTip(event, 'fs197', 1593)" class="id">MapSlim</span><span class="pn">(</span><span class="pn">)</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs299', 1594)" onmouseover="showTip(event, 'fs299', 1594)" class="id">dict</span> <span class="o">=</span> <span class="id">Dictionary</span><span class="pn">(</span><span class="pn">)</span>
<span class="k">let!</span> <span class="id">n</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs184', 1595)" onmouseover="showTip(event, 'fs184', 1595)" class="id">Gen</span><span class="pn">.</span><span class="id">int</span><span class="pn">.</span><span class="pn">[</span><span class="n">1..</span><span class="n">100</span><span class="pn">]</span>
<span class="k">let!</span> <span class="id">keys</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs184', 1596)" onmouseover="showTip(event, 'fs184', 1596)" class="id">Gen</span><span class="pn">.</span><span class="id">array</span><span class="pn">.</span><span class="pn">[</span><span class="id">n</span><span class="pn">]</span> <span onmouseout="hideTip(event, 'fs184', 1597)" onmouseover="showTip(event, 'fs184', 1597)" class="id">Gen</span><span class="pn">.</span><span class="id">int</span><span class="pn">.</span><span class="pn">[</span><span class="o">-</span><span class="n">1000..</span><span class="n">1000</span><span class="pn">]</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs184', 1598)" onmouseover="showTip(event, 'fs184', 1598)" class="id">Gen</span><span class="pn">.</span><span class="id">map</span> <span class="pn">(</span>
<span onmouseout="hideTip(event, 'fs105', 1599)" onmouseover="showTip(event, 'fs105', 1599)" class="id">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs300', 1600)" onmouseover="showTip(event, 'fs300', 1600)" class="id">mapi</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">i</span> <span class="id">h</span> <span class="k">-></span>
<span class="k">let</span> <span class="id">k</span> <span class="o">=</span> <span class="id">KeyWithHash</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs13', 1601)" onmouseover="showTip(event, 'fs13', 1601)" class="id">uint64</span> <span class="id">i</span><span class="pn">,</span> <span class="id">h</span><span class="pn">)</span>
<span class="id">ms</span><span class="pn">.</span><span class="id">Set</span><span class="pn">(</span><span class="id">k</span><span class="pn">,</span><span class="id">k</span><span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs299', 1602)" onmouseover="showTip(event, 'fs299', 1602)" class="id">dict</span><span class="pn">.</span><span class="id">Add</span><span class="pn">(</span><span class="id">k</span><span class="pn">,</span><span class="id">k</span><span class="pn">)</span>
<span class="id">k</span>
<span class="pn">)</span>
<span class="pn">)</span>
<span class="id">Test</span><span class="pn">.</span><span class="id">faster</span>
<span class="pn">(</span><span class="k">fun</span> <span class="pn">(</span><span class="pn">)</span> <span class="k">-></span>
<span class="k">for</span> <span class="id">i</span> <span class="o">=</span> <span class="n">0</span> <span class="k">to</span> <span class="id">n</span><span class="o">-</span><span class="n">1</span> <span class="k">do</span>
<span class="id">ms</span><span class="pn">.</span><span class="id">GetOption</span> <span class="id">keys</span><span class="pn">.</span><span class="pn">[</span><span class="id">i</span><span class="pn">]</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs220', 1603)" onmouseover="showTip(event, 'fs220', 1603)" class="id">ignore</span>
<span class="pn">)</span>
<span class="pn">(</span><span class="k">fun</span> <span class="pn">(</span><span class="pn">)</span> <span class="k">-></span>
<span class="k">for</span> <span class="id">i</span> <span class="o">=</span> <span class="n">0</span> <span class="k">to</span> <span class="id">n</span><span class="o">-</span><span class="n">1</span> <span class="k">do</span>
<span onmouseout="hideTip(event, 'fs299', 1604)" onmouseover="showTip(event, 'fs299', 1604)" class="id">dict</span><span class="pn">.</span><span class="id">TryGetValue</span> <span class="id">keys</span><span class="pn">.</span><span class="pn">[</span><span class="id">i</span><span class="pn">]</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs220', 1605)" onmouseover="showTip(event, 'fs220', 1605)" class="id">ignore</span>
<span class="pn">)</span>
<span class="s">"get"</span>
<span class="pn">}</span>
<span class="c">// ...</span>
<span class="pn">}</span>
<span class="c">// ...</span>
<span class="pn">}</span>
</code></pre>
<img src="/public/test/faster.png" title="faster"/>
<ul>
<li>Tests are run in parallel using continuations - Fine grained, in test asynchronous code to make each test faster.</li>
</ul>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="id">test</span> <span class="s">"reference"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">getTest</span> <span class="id">name</span> <span class="pn">(</span><span class="id">gen</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs184', 1606)" onmouseover="showTip(event, 'fs184', 1606)" class="id">Gen</span><span class="pn"><</span><span class="id">'</span><span class="id">a</span><span class="pn">></span><span class="pn">)</span> <span class="o">=</span> <span class="id">test</span> <span class="id">name</span> <span class="pn">{</span>
<span class="k">let!</span> <span class="id">items</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs184', 1607)" onmouseover="showTip(event, 'fs184', 1607)" class="id">Gen</span><span class="pn">.</span><span class="id">tuple</span> <span class="id">gen</span> <span onmouseout="hideTip(event, 'fs184', 1608)" onmouseover="showTip(event, 'fs184', 1608)" class="id">Gen</span><span class="pn">.</span><span class="id">int</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs184', 1609)" onmouseover="showTip(event, 'fs184', 1609)" class="id">Gen</span><span class="pn">.</span><span class="id">list</span><span class="pn">.</span><span class="pn">[</span><span class="o">..</span><span class="n">100</span><span class="pn">]</span>
<span class="k">let!</span> <span class="id">check</span> <span class="o">=</span> <span class="id">gen</span>
<span class="k">let!</span> <span class="id">expectedFork</span> <span class="o">=</span>
<span class="id">io</span> <span class="pn">{</span>
<span class="k">return</span>
<span onmouseout="hideTip(event, 'fs64', 1610)" onmouseover="showTip(event, 'fs64', 1610)" class="id">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs65', 1611)" onmouseover="showTip(event, 'fs65', 1611)" class="id">fold</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">m</span> <span class="pn">(</span><span class="id">k</span><span class="pn">,</span><span class="id">v</span><span class="pn">)</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs301', 1612)" onmouseover="showTip(event, 'fs301', 1612)" class="id">Map</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs302', 1613)" onmouseover="showTip(event, 'fs302', 1613)" class="id">add</span> <span class="id">k</span> <span class="id">v</span> <span class="id">m</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs301', 1614)" onmouseover="showTip(event, 'fs301', 1614)" class="id">Map</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs303', 1615)" onmouseover="showTip(event, 'fs303', 1615)" class="id">empty</span> <span class="id">items</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs301', 1616)" onmouseover="showTip(event, 'fs301', 1616)" class="id">Map</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs304', 1617)" onmouseover="showTip(event, 'fs304', 1617)" class="id">tryFind</span> <span class="id">check</span>
<span class="pn">}</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs193', 1618)" onmouseover="showTip(event, 'fs193', 1618)" class="id">IO</span><span class="pn">.</span><span class="id">fork</span>
<span class="k">let</span> <span class="id">actual</span> <span class="o">=</span>
<span class="k">let</span> <span class="id">ms</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs197', 1619)" onmouseover="showTip(event, 'fs197', 1619)" class="id">MapSlim</span><span class="pn">(</span><span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs64', 1620)" onmouseover="showTip(event, 'fs64', 1620)" class="id">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs272', 1621)" onmouseover="showTip(event, 'fs272', 1621)" class="id">iter</span> <span class="id">ms</span><span class="pn">.</span><span class="id">Set</span> <span class="id">items</span>
<span class="id">ms</span><span class="pn">.</span><span class="id">GetOption</span> <span class="id">check</span>
<span class="o">|></span> <span class="k">function</span> <span class="pn">|</span> <span onmouseout="hideTip(event, 'fs158', 1622)" onmouseover="showTip(event, 'fs158', 1622)" class="id">ValueSome</span> <span class="id">i</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs40', 1623)" onmouseover="showTip(event, 'fs40', 1623)" class="id">Some</span> <span class="id">i</span> <span class="pn">|</span> <span onmouseout="hideTip(event, 'fs159', 1624)" onmouseover="showTip(event, 'fs159', 1624)" class="id">ValueNone</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs41', 1625)" onmouseover="showTip(event, 'fs41', 1625)" class="id">None</span>
<span class="k">let!</span> <span class="id">expected</span> <span class="o">=</span> <span class="id">expectedFork</span>
<span class="id">Test</span><span class="pn">.</span><span class="id">equal</span> <span class="id">actual</span> <span class="id">expected</span> <span class="s">"check value equal"</span>
<span class="pn">}</span>
<span class="id">getTest</span> <span class="s">"get byte"</span> <span onmouseout="hideTip(event, 'fs184', 1626)" onmouseover="showTip(event, 'fs184', 1626)" class="id">Gen</span><span class="pn">.</span><span class="id">byte</span>
<span class="id">getTest</span> <span class="s">"get char"</span> <span onmouseout="hideTip(event, 'fs184', 1627)" onmouseover="showTip(event, 'fs184', 1627)" class="id">Gen</span><span class="pn">.</span><span class="id">char</span>
<span class="id">getTest</span> <span class="s">"get int"</span> <span onmouseout="hideTip(event, 'fs184', 1628)" onmouseover="showTip(event, 'fs184', 1628)" class="id">Gen</span><span class="pn">.</span><span class="id">int</span><span class="pn">.</span><span class="pn">[</span><span class="o">..</span><span class="n">10</span><span class="pn">]</span>
<span class="id">getTest</span> <span class="s">"get uint"</span> <span onmouseout="hideTip(event, 'fs184', 1629)" onmouseover="showTip(event, 'fs184', 1629)" class="id">Gen</span><span class="pn">.</span><span class="id">uint</span><span class="pn">.</span><span class="pn">[</span><span class="o">..</span><span class="n">10u</span><span class="pn">]</span>
<span class="id">getTest</span> <span class="s">"get string"</span> <span onmouseout="hideTip(event, 'fs184', 1630)" onmouseover="showTip(event, 'fs184', 1630)" class="id">Gen</span><span class="pn">.</span><span class="id">string</span>
<span class="pn">}</span>
</code></pre>
<img src="/public/test/help.png" title="help"/>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p>The prototype currently has no dependencies and is a single file for the library, one for Gen and one for Test.
They can easily be copied into a project to try them out, and new Gen and Test functions added.</p>
<p>The library is still in an early stage.
It is missing more tests, label functionality, async io and needs a tidy and performance work.</p>
<p>I am keen to share it now to see if there are further ideas.
Some of the functionality if successful could be added to existing projects or it could form its own.
It is unclear at this point.</p>
<p>The code can be found <a href="https://github.com/AnthonyLloyd/Test">here</a>.</p>
<div class="tip" id="fs1">module IRT</div>
<div class="tip" id="fs2">namespace System</div>
<div class="tip" id="fs3">namespace System.Threading</div>
<div class="tip" id="fs4">namespace System.Diagnostics</div>
<div class="tip" id="fs5">namespace System.Globalization</div>
<div class="tip" id="fs6">namespace System.Runtime</div>
<div class="tip" id="fs7">namespace System.Runtime.CompilerServices</div>
<div class="tip" id="fs8">namespace System.Runtime.InteropServices</div>
<div class="tip" id="fs9">namespace Microsoft</div>
<div class="tip" id="fs10">namespace Microsoft.FSharp</div>
<div class="tip" id="fs11">namespace Microsoft.FSharp.Core</div>
<div class="tip" id="fs12">PCG.Inc: uint64</div>
<div class="tip" id="fs13">Multiple items<br />val uint64 : value:'T -> uint64 (requires member op_Explicit)<br /><br />--------------------<br />type uint64 = UInt64</div>
<div class="tip" id="fs14">PCG.State: uint64</div>
<div class="tip" id="fs15">val inc : uint64</div>
<div class="tip" id="fs16">val state : uint64</div>
<div class="tip" id="fs17">val stream : int</div>
<div class="tip" id="fs18">Multiple items<br />val int : value:'T -> int (requires member op_Explicit)<br /><br />--------------------<br />type int = int32<br /><br />--------------------<br />type int<'Measure> = int</div>
<div class="tip" id="fs19">val seed : uint64</div>
<div class="tip" id="fs20">Multiple items<br />type PCG =<br />  new : stream:int -> PCG<br />  private new : inc:uint64 * state:uint64 -> PCG<br />  new : stream:int * seed:uint64 -> PCG<br />  val Inc: uint64<br />  val mutable State: uint64<br />  member Next : unit -> uint32<br />  member Next : maxExclusive:int -> int<br />  member Next64 : unit -> uint64<br />  member Next64 : maxExclusive:int64 -> int64<br />  override ToString : unit -> string<br />  ...<br /><br />--------------------<br />new : stream:int -> PCG<br />new : stream:int * seed:uint64 -> PCG<br />private new : inc:uint64 * state:uint64 -> PCG</div>
<div class="tip" id="fs21">Multiple items<br />type Stopwatch =<br />  new : unit -> Stopwatch<br />  member Elapsed : TimeSpan<br />  member ElapsedMilliseconds : int64<br />  member ElapsedTicks : int64<br />  member IsRunning : bool<br />  member Reset : unit -> unit<br />  member Restart : unit -> unit<br />  member Start : unit -> unit<br />  member Stop : unit -> unit<br />  static val Frequency : int64<br />  ...<br /><br />--------------------<br />Stopwatch() : Stopwatch</div>
<div class="tip" id="fs22">Stopwatch.GetTimestamp() : int64</div>
<div class="tip" id="fs23">val i : PCG</div>
<div class="tip" id="fs24">val sprintf : format:Printf.StringFormat<'T> -> 'T</div>
<div class="tip" id="fs25">val s : string</div>
<div class="tip" id="fs26">Multiple items<br />val string : value:'T -> string<br /><br />--------------------<br />type string = String</div>
<div class="tip" id="fs27">val l : int</div>
<div class="tip" id="fs28">property String.Length: int</div>
<div class="tip" id="fs29">val mutable stream : int</div>
<div class="tip" id="fs30">module Unchecked<br /><br />from Microsoft.FSharp.Core.Operators</div>
<div class="tip" id="fs31">val defaultof<'T> : 'T</div>
<div class="tip" id="fs32">val mutable state : uint64</div>
<div class="tip" id="fs33">type Int32 =<br />  struct<br />    member CompareTo : value:obj -> int + 1 overload<br />    member Equals : obj:obj -> bool + 1 overload<br />    member GetHashCode : unit -> int<br />    member GetTypeCode : unit -> TypeCode<br />    member ToString : unit -> string + 3 overloads<br />    static val MaxValue : int<br />    static val MinValue : int<br />    static member Parse : s:string -> int + 3 overloads<br />    static member TryParse : s:string * result:int -> bool + 1 overload<br />  end</div>
<div class="tip" id="fs34">Int32.TryParse(s: string, result: byref<int>) : bool<br />Int32.TryParse(s: string, style: NumberStyles, provider: IFormatProvider, result: byref<int>) : bool</div>
<div class="tip" id="fs35">String.Substring(startIndex: int) : string<br />String.Substring(startIndex: int, length: int) : string</div>
<div class="tip" id="fs36">type NumberStyles =<br />  | None = 0<br />  | AllowLeadingWhite = 1<br />  | AllowTrailingWhite = 2<br />  | AllowLeadingSign = 4<br />  | AllowTrailingSign = 8<br />  | AllowParentheses = 16<br />  | AllowDecimalPoint = 32<br />  | AllowThousands = 64<br />  | AllowExponent = 128<br />  | AllowCurrencySymbol = 256<br />  ...</div>
<div class="tip" id="fs37">field NumberStyles.HexNumber: NumberStyles = 515</div>
<div class="tip" id="fs38">type UInt64 =<br />  struct<br />    member CompareTo : value:obj -> int + 1 overload<br />    member Equals : obj:obj -> bool + 1 overload<br />    member GetHashCode : unit -> int<br />    member GetTypeCode : unit -> TypeCode<br />    member ToString : unit -> string + 3 overloads<br />    static val MaxValue : uint64<br />    static val MinValue : uint64<br />    static member Parse : s:string -> uint64 + 3 overloads<br />    static member TryParse : s:string * result:uint64 -> bool + 1 overload<br />  end</div>
<div class="tip" id="fs39">UInt64.TryParse(s: string, result: byref<uint64>) : bool<br />UInt64.TryParse(s: string, style: NumberStyles, provider: IFormatProvider, result: byref<uint64>) : bool</div>
<div class="tip" id="fs40">union case Option.Some: Value: 'T -> Option<'T></div>
<div class="tip" id="fs41">union case Option.None: Option<'T></div>
<div class="tip" id="fs42">val p : PCG</div>
<div class="tip" id="fs43">val oldstate : uint64</div>
<div class="tip" id="fs44">val xorshifted : uint32</div>
<div class="tip" id="fs45">Multiple items<br />val uint32 : value:'T -> uint32 (requires member op_Explicit)<br /><br />--------------------<br />type uint32 = UInt32</div>
<div class="tip" id="fs46">val rot : int</div>
<div class="tip" id="fs47">member PCG.Next : unit -> uint32<br />member PCG.Next : maxExclusive:int -> int</div>
<div class="tip" id="fs48">val maxExclusive : int</div>
<div class="tip" id="fs49">val bound : uint32</div>
<div class="tip" id="fs50">val threshold : uint32</div>
<div class="tip" id="fs51">val find : (unit -> int)</div>
<div class="tip" id="fs52">val r : uint32</div>
<div class="tip" id="fs53">val maxExclusive : int64</div>
<div class="tip" id="fs54">Multiple items<br />val int64 : value:'T -> int64 (requires member op_Explicit)<br /><br />--------------------<br />type int64 = Int64<br /><br />--------------------<br />type int64<'Measure> = int64</div>
<div class="tip" id="fs55">val bound : uint64</div>
<div class="tip" id="fs56">val threshold : uint64</div>
<div class="tip" id="fs57">val find : (unit -> int64)</div>
<div class="tip" id="fs58">val r : uint64</div>
<div class="tip" id="fs59">member PCG.Next64 : unit -> uint64<br />member PCG.Next64 : maxExclusive:int64 -> int64</div>
<div class="tip" id="fs60">Multiple items<br />module Result<br /><br />from Microsoft.FSharp.Core<br /><br />--------------------<br />[<Struct>]<br />type Result<'T,'TError> =<br />  | Ok of ResultValue: 'T<br />  | Error of ErrorValue: 'TError</div>
<div class="tip" id="fs61">val private traverse : f:('a -> Result<'b,'c>) -> list:'a list -> Result<'b list,'c list></div>
<div class="tip" id="fs62">val f : ('a -> Result<'b,'c>)</div>
<div class="tip" id="fs63">Multiple items<br />val list : 'a list<br /><br />--------------------<br />type 'T list = List<'T></div>
<div class="tip" id="fs64">Multiple items<br />module List<br /><br />from Microsoft.FSharp.Collections<br /><br />--------------------<br />type List<'T> =<br />  | ( [] )<br />  | ( :: ) of Head: 'T * Tail: 'T list<br />    interface IReadOnlyList<'T><br />    interface IReadOnlyCollection<'T><br />    interface IEnumerable<br />    interface IEnumerable<'T><br />    member GetSlice : startIndex:int option * endIndex:int option -> 'T list<br />    member Head : 'T<br />    member IsEmpty : bool<br />    member Item : index:int -> 'T with get<br />    member Length : int<br />    member Tail : 'T list<br />    ...</div>
<div class="tip" id="fs65">val fold : folder:('State -> 'T -> 'State) -> state:'State -> list:'T list -> 'State</div>
<div class="tip" id="fs66">[<Struct>]<br />val s : Result<'b list,'c list></div>
<div class="tip" id="fs67">val i : 'a</div>
<div class="tip" id="fs68">union case Result.Ok: ResultValue: 'T -> Result<'T,'TError></div>
<div class="tip" id="fs69">val l : 'b list</div>
<div class="tip" id="fs70">val h : 'b</div>
<div class="tip" id="fs71">union case Result.Error: ErrorValue: 'TError -> Result<'T,'TError></div>
<div class="tip" id="fs72">val l : 'c list</div>
<div class="tip" id="fs73">val e : 'c</div>
<div class="tip" id="fs74">val h : 'c</div>
<div class="tip" id="fs75">val private sequence : list:Result<'a,'b> list -> Result<'a list,'b list></div>
<div class="tip" id="fs76">Multiple items<br />val list : Result<'a,'b> list<br /><br />--------------------<br />type 'T list = List<'T></div>
<div class="tip" id="fs77">val id : x:'T -> 'T</div>
<div class="tip" id="fs78">namespace System.Text</div>
<div class="tip" id="fs79">union case Text.For: string -> Text</div>
<div class="tip" id="fs80">union case Text.Grey: string -> Text</div>
<div class="tip" id="fs81">union case Text.Red: string -> Text</div>
<div class="tip" id="fs82">union case Text.BrightRed: string -> Text</div>
<div class="tip" id="fs83">union case Text.Green: string -> Text</div>
<div class="tip" id="fs84">union case Text.BrightGreen: string -> Text</div>
<div class="tip" id="fs85">union case Text.Yellow: string -> Text</div>
<div class="tip" id="fs86">union case Text.BrightYellow: string -> Text</div>
<div class="tip" id="fs87">union case Text.Blue: string -> Text</div>
<div class="tip" id="fs88">union case Text.BrightBlue: string -> Text</div>
<div class="tip" id="fs89">union case Text.Magenta: string -> Text</div>
<div class="tip" id="fs90">union case Text.BrightMagenta: string -> Text</div>
<div class="tip" id="fs91">union case Text.Cyan: string -> Text</div>
<div class="tip" id="fs92">union case Text.BrightCyan: string -> Text</div>
<div class="tip" id="fs93">Multiple items<br />union case Text.Text: struct (Text * Text) -> Text<br /><br />--------------------<br />namespace System.Text<br /><br />--------------------<br />type Text =<br />  | For of string<br />  | Grey of string<br />  | Red of string<br />  | BrightRed of string<br />  | Green of string<br />  | BrightGreen of string<br />  | Yellow of string<br />  | BrightYellow of string<br />  | Blue of string<br />  | BrightBlue of string<br />  ...<br />    static member ( + ) : t1:Text * t2:Text -> Text</div>
<div class="tip" id="fs94">val t1 : Text</div>
<div class="tip" id="fs95">val t2 : Text</div>
<div class="tip" id="fs96">Multiple items<br />type LiteralAttribute =<br />  inherit Attribute<br />  new : unit -> LiteralAttribute<br /><br />--------------------<br />new : unit -> LiteralAttribute</div>
<div class="tip" id="fs97">val private reset : string</div>
<div class="tip" id="fs98">val private toANSIList : t:Text -> string list</div>
<div class="tip" id="fs99">val t : Text</div>
<div class="tip" id="fs100">val toANSI : t:Text -> string</div>
<div class="tip" id="fs101">Multiple items<br />type String =<br />  new : value:char -> string + 7 overloads<br />  member Chars : int -> char<br />  member Clone : unit -> obj<br />  member CompareTo : value:obj -> int + 1 overload<br />  member Contains : value:string -> bool<br />  member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit<br />  member EndsWith : value:string -> bool + 2 overloads<br />  member Equals : obj:obj -> bool + 2 overloads<br />  member GetEnumerator : unit -> CharEnumerator<br />  member GetHashCode : unit -> int<br />  ...<br /><br />--------------------<br />String(value: nativeptr<char>) : String<br />String(value: nativeptr<sbyte>) : String<br />String(value: char []) : String<br />String(c: char, count: int) : String<br />String(value: nativeptr<char>, startIndex: int, length: int) : String<br />String(value: nativeptr<sbyte>, startIndex: int, length: int) : String<br />String(value: char [], startIndex: int, length: int) : String<br />String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: Text.Encoding) : String</div>
<div class="tip" id="fs102">String.Concat([<ParamArray>] values: string []) : string<br />   <em>(+0 other overloads)</em><br />String.Concat(values: Collections.Generic.IEnumerable<string>) : string<br />   <em>(+0 other overloads)</em><br />String.Concat<'T>(values: Collections.Generic.IEnumerable<'T>) : string<br />   <em>(+0 other overloads)</em><br />String.Concat([<ParamArray>] args: obj []) : string<br />   <em>(+0 other overloads)</em><br />String.Concat(arg0: obj) : string<br />   <em>(+0 other overloads)</em><br />String.Concat(str0: string, str1: string) : string<br />   <em>(+0 other overloads)</em><br />String.Concat(arg0: obj, arg1: obj) : string<br />   <em>(+0 other overloads)</em><br />String.Concat(str0: string, str1: string, str2: string) : string<br />   <em>(+0 other overloads)</em><br />String.Concat(arg0: obj, arg1: obj, arg2: obj) : string<br />   <em>(+0 other overloads)</em><br />String.Concat(str0: string, str1: string, str2: string, str3: string) : string<br />   <em>(+0 other overloads)</em></div>
<div class="tip" id="fs103">ListSlim.count: int</div>
<div class="tip" id="fs104">ListSlim.entries: 'k []</div>
<div class="tip" id="fs105">type Array =<br />  member Clone : unit -> obj<br />  member CopyTo : array:Array * index:int -> unit + 1 overload<br />  member GetEnumerator : unit -> IEnumerator<br />  member GetLength : dimension:int -> int<br />  member GetLongLength : dimension:int -> int64<br />  member GetLowerBound : dimension:int -> int<br />  member GetUpperBound : dimension:int -> int<br />  member GetValue : [<ParamArray>] indices:int[] -> obj + 7 overloads<br />  member Initialize : unit -> unit<br />  member IsFixedSize : bool<br />  ...</div>
<div class="tip" id="fs106">val empty<'T> : 'T []</div>
<div class="tip" id="fs107">val capacity : int</div>
<div class="tip" id="fs108">val zeroCreate : count:int -> 'T []</div>
<div class="tip" id="fs109">val m : ListSlim<'k></div>
<div class="tip" id="fs110">val i : int</div>
<div class="tip" id="fs111">val set : elements:seq<'T> -> Set<'T> (requires comparison)</div>
<div class="tip" id="fs112">val v : 'k</div>
<div class="tip" id="fs113">val key : 'k</div>
<div class="tip" id="fs114">property Array.Length: int</div>
<div class="tip" id="fs115">val newEntries : 'k []</div>
<div class="tip" id="fs116">Array.Copy(sourceArray: Array, destinationArray: Array, length: int64) : unit<br />Array.Copy(sourceArray: Array, destinationArray: Array, length: int) : unit<br />Array.Copy(sourceArray: Array, sourceIndex: int64, destinationArray: Array, destinationIndex: int64, length: int64) : unit<br />Array.Copy(sourceArray: Array, sourceIndex: int, destinationArray: Array, destinationIndex: int, length: int) : unit</div>
<div class="tip" id="fs117">val init : count:int -> initializer:(int -> 'T) -> 'T []</div>
<div class="tip" id="fs118">val get : array:'T [] -> index:int -> 'T</div>
<div class="tip" id="fs119">val init : length:int -> initializer:(int -> 'T) -> 'T list</div>
<div class="tip" id="fs120">Multiple items<br />type StructAttribute =<br />  inherit Attribute<br />  new : unit -> StructAttribute<br /><br />--------------------<br />new : unit -> StructAttribute</div>
<div class="tip" id="fs121">type private Entry<'k,'v> =<br />  struct<br />    val mutable bucket: int<br />    val mutable key: 'k<br />    val mutable value: 'v<br />    val mutable next: int<br />  end</div>
<div class="tip" id="fs122">Entry.bucket: int</div>
<div class="tip" id="fs123">Entry.key: 'k</div>
<div class="tip" id="fs124">Entry.value: 'v</div>
<div class="tip" id="fs125">Entry.next: int</div>
<div class="tip" id="fs126">Multiple items<br />type private InitialHolder<'k,'v> =<br />  new : unit -> InitialHolder<'k,'v><br />  static member Initial : Entry<'k,'v> []<br /><br />--------------------<br />private new : unit -> InitialHolder<'k,'v></div>
<div class="tip" id="fs127">val initial : Entry<'k,'v> []</div>
<div class="tip" id="fs128">type IEquatable<'T> =<br />  member Equals : other:'T -> bool</div>
<div class="tip" id="fs129">MapSlim.count: int</div>
<div class="tip" id="fs130">MapSlim.entries: Entry<'k,'v> []</div>
<div class="tip" id="fs131">property InitialHolder.Initial: Entry<'k,'v> []</div>
<div class="tip" id="fs132">val powerOf2 : (int -> int)</div>
<div class="tip" id="fs133">val v : int</div>
<div class="tip" id="fs134">val twos : (int -> int)</div>
<div class="tip" id="fs135">val m : MapSlim<'k,'v> (requires equality and 'k :> IEquatable<'k>)</div>
<div class="tip" id="fs136">val oldEntries : Entry<'k,'v> [] (requires equality and 'k :> IEquatable<'k>)</div>
<div class="tip" id="fs137">val entries : Entry<'k,'v> [] (requires equality and 'k :> IEquatable<'k>)</div>
<div class="tip" id="fs138">val bi : int</div>
<div class="tip" id="fs139">Multiple items<br />type MethodImplAttribute =<br />  inherit Attribute<br />  new : unit -> MethodImplAttribute + 2 overloads<br />  val MethodCodeType : MethodCodeType<br />  member Value : MethodImplOptions<br /><br />--------------------<br />MethodImplAttribute() : MethodImplAttribute<br />MethodImplAttribute(methodImplOptions: MethodImplOptions) : MethodImplAttribute<br />MethodImplAttribute(value: int16) : MethodImplAttribute</div>
<div class="tip" id="fs140">type MethodImplOptions =<br />  | Unmanaged = 4<br />  | ForwardRef = 16<br />  | PreserveSig = 128<br />  | InternalCall = 4096<br />  | Synchronized = 32<br />  | NoInlining = 8<br />  | AggressiveInlining = 256<br />  | NoOptimization = 64</div>
<div class="tip" id="fs141">field MethodImplOptions.NoInlining: MethodImplOptions = 8</div>
<div class="tip" id="fs142">val key : 'k (requires equality and 'k :> IEquatable<'k>)</div>
<div class="tip" id="fs143">val hashCode : int</div>
<div class="tip" id="fs144">member private MapSlim.Resize : unit -> unit</div>
<div class="tip" id="fs145">val bucketIndex : int</div>
<div class="tip" id="fs146">val value : 'v</div>
<div class="tip" id="fs147">Object.GetHashCode() : int</div>
<div class="tip" id="fs148">val mutable i : int</div>
<div class="tip" id="fs149">val not : value:bool -> bool</div>
<div class="tip" id="fs150">Object.Equals(obj: obj) : bool<br />IEquatable.Equals(other: 'k) : bool</div>
<div class="tip" id="fs151">val v : byref<'v></div>
<div class="tip" id="fs152">member private MapSlim.AddKey : key:'k * hashCode:int -> byref<'v></div>
<div class="tip" id="fs153">type byref<'T> = (# "<Common IL Type Omitted>" #)</div>
<div class="tip" id="fs154">val added : outref<bool></div>
<div class="tip" id="fs155">type bool = Boolean</div>
<div class="tip" id="fs156">type outref<'T> = outref<'T></div>
<div class="tip" id="fs157">[<Struct>]<br />type 'T voption = ValueOption<'T></div>
<div class="tip" id="fs158">union case ValueOption.ValueSome: 'T -> ValueOption<'T></div>
<div class="tip" id="fs159">union case ValueOption.ValueNone: ValueOption<'T></div>
<div class="tip" id="fs160">val entries : Entry<'k,'v> (requires equality and 'k :> IEquatable<'k>)</div>
<div class="tip" id="fs161">Multiple items<br />type AllowNullLiteralAttribute =<br />  inherit Attribute<br />  new : unit -> AllowNullLiteralAttribute<br />  new : value:bool -> AllowNullLiteralAttribute<br />  member Value : bool<br /><br />--------------------<br />new : unit -> AllowNullLiteralAttribute<br />new : value:bool -> AllowNullLiteralAttribute</div>
<div class="tip" id="fs162">Size.I: uint64</div>
<div class="tip" id="fs163">Size.L: Size list</div>
<div class="tip" id="fs164">Multiple items<br />type Size =<br />  interface IComparable<br />  interface IComparable<Size><br />  new : i:uint64 * l:Size list -> Size<br />  val I: uint64<br />  val L: Size list<br />  override Equals : y:obj -> bool<br />  override GetHashCode : unit -> int<br />  static member zero : Size<br /><br />--------------------<br />new : i:uint64 * l:Size list -> Size</div>
<div class="tip" id="fs165">type 'T list = List<'T></div>
<div class="tip" id="fs166">val i : uint64</div>
<div class="tip" id="fs167">val l : Size list</div>
<div class="tip" id="fs168">val x : Size</div>
<div class="tip" id="fs169">val y : obj</div>
<div class="tip" id="fs170">Multiple items<br />type IComparable =<br />  member CompareTo : obj:obj -> int<br /><br />--------------------<br />type IComparable<'T> =<br />  member CompareTo : other:'T -> int</div>
<div class="tip" id="fs171">val y : Size</div>
<div class="tip" id="fs172">val compare : (Size -> Size -> int64)</div>
<div class="tip" id="fs173">val i : int64</div>
<div class="tip" id="fs174">val fold2 : folder:('State -> 'T1 -> 'T2 -> 'State) -> state:'State -> list1:'T1 list -> list2:'T2 list -> 'State</div>
<div class="tip" id="fs175">val s : int64</div>
<div class="tip" id="fs176">val sign : value:'T -> int (requires member get_Sign)</div>
<div class="tip" id="fs177">type Gen<'a> =<br />  interface<br />    abstract member Gen : PCG -> 'a * Size<br />  end</div>
<div class="tip" id="fs178">Multiple items<br />type PCG =<br />  new : stream:int -> PCG<br />  private new : inc:uint64 * state:uint64 -> PCG<br />  new : stream:int * seed:uint64 -> PCG<br />  val Inc: uint64<br />  val mutable State: uint64<br />  member Next : unit -> uint32<br />  member Next : maxExclusive:int -> int<br />  member Next64 : unit -> uint64<br />  member Next64 : maxExclusive:int64 -> int64<br />  override ToString : unit -> string<br />  ...<br /><br />--------------------<br />new : stream:int -> PCG<br />new : stream:int * seed:uint64 -> PCG</div>
<div class="tip" id="fs179">type GenRange<'a,'b> =<br />  interface<br />    inherit Gen<'b><br />    abstract member GetSlice : 'a option * 'a option -> Gen<'b><br />  end</div>
<div class="tip" id="fs180">type 'T option = Option<'T></div>
<div class="tip" id="fs181">Multiple items<br />type GenBuilder =<br />  new : unit -> GenBuilder<br />  new : Gen:obj -> GenBuilder<br /><br />--------------------<br />new : unit -> GenBuilder<br />new : Gen:obj -> GenBuilder</div>
<div class="tip" id="fs182">val g : unit * Size</div>
<div class="tip" id="fs183">property Size.zero: Size</div>
<div class="tip" id="fs184">Multiple items<br />val Gen : obj<br /><br />--------------------<br />type Gen<'a> =<br />  interface<br />    abstract member Gen : PCG -> 'a * Size<br />  end</div>
<div class="tip" id="fs185">val fst : tuple:('T1 * 'T2) -> 'T1</div>
<div class="tip" id="fs186">type obj = Object</div>
<div class="tip" id="fs187">Multiple items<br />union case Text.Text: struct (Text * Text) -> Text<br /><br />--------------------<br />module Text<br /><br />from IRT<br /><br />--------------------<br />namespace System.Text<br /><br />--------------------<br />type Text =<br />  | For of string<br />  | Grey of string<br />  | Red of string<br />  | BrightRed of string<br />  | Green of string<br />  | BrightGreen of string<br />  | Yellow of string<br />  | BrightYellow of string<br />  | Blue of string<br />  | BrightBlue of string<br />  ...<br />    static member ( + ) : t1:Text * t2:Text -> Text</div>
<div class="tip" id="fs188">Multiple items<br />val Failure : message:string -> exn<br /><br />--------------------<br />active recognizer Failure: exn -> string option</div>
<div class="tip" id="fs189">Multiple items<br />type Exception =<br />  new : unit -> Exception + 2 overloads<br />  member Data : IDictionary<br />  member GetBaseException : unit -> Exception<br />  member GetObjectData : info:SerializationInfo * context:StreamingContext -> unit<br />  member GetType : unit -> Type<br />  member HResult : int with get, set<br />  member HelpLink : string with get, set<br />  member InnerException : Exception<br />  member Message : string<br />  member Source : string with get, set<br />  ...<br /><br />--------------------<br />Exception() : Exception<br />Exception(message: string) : Exception<br />Exception(message: string, innerException: exn) : Exception</div>
<div class="tip" id="fs190">type exn = Exception</div>
<div class="tip" id="fs191">Multiple items<br />type ListSlim<'k> =<br />  new : unit -> ListSlim<'k><br />  new : capacity:int -> ListSlim<'k><br />  val mutable private count: int<br />  val mutable private entries: 'k []<br />  member Add : key:'k -> int<br />  member ToArray : unit -> 'k []<br />  member ToList : unit -> 'k list<br />  member Count : int<br />  member Item : i:int -> 'k with get<br />  member Item : i:int -> 'k with set<br /><br />--------------------<br />new : unit -> ListSlim<'k><br />new : capacity:int -> ListSlim<'k></div>
<div class="tip" id="fs192">val exists : predicate:('T -> bool) -> list:'T list -> bool</div>
<div class="tip" id="fs193">namespace System.IO</div>
<div class="tip" id="fs194">val failwith : message:string -> 'T</div>
<div class="tip" id="fs195">Multiple items<br />type AutoOpenAttribute =<br />  inherit Attribute<br />  new : unit -> AutoOpenAttribute<br />  new : path:string -> AutoOpenAttribute<br />  member Path : string<br /><br />--------------------<br />new : unit -> AutoOpenAttribute<br />new : path:string -> AutoOpenAttribute</div>
<div class="tip" id="fs196">type unit = Unit</div>
<div class="tip" id="fs197">Multiple items<br />type MapSlim<'k,'v (requires equality and 'k :> IEquatable<'k>)> =<br />  new : unit -> MapSlim<'k,'v><br />  new : capacity:int -> MapSlim<'k,'v><br />  val mutable private count: int<br />  val mutable private entries: Entry<'k,'v> []<br />  member private AddKey : key:'k * hashCode:int -> byref<'v><br />  member GetOption : key:'k -> 'v voption<br />  member GetRef : key:'k -> byref<'v><br />  member GetRef : key:'k * added:outref<bool> -> byref<'v><br />  member Item : i:int -> 'k * 'v<br />  member Key : i:int -> 'k<br />  ...<br /><br />--------------------<br />new : unit -> MapSlim<'k,'v><br />new : capacity:int -> MapSlim<'k,'v></div>
<div class="tip" id="fs198">val map : mapping:('T -> 'U) -> list:'T list -> 'U list</div>
<div class="tip" id="fs199">Multiple items<br />type CallerLineNumberAttribute =<br />  inherit Attribute<br />  new : unit -> CallerLineNumberAttribute<br /><br />--------------------<br />CallerLineNumberAttribute() : CallerLineNumberAttribute</div>
<div class="tip" id="fs200">Multiple items<br />type OptionalAttribute =<br />  inherit Attribute<br />  new : unit -> OptionalAttribute<br /><br />--------------------<br />OptionalAttribute() : OptionalAttribute</div>
<div class="tip" id="fs201">Multiple items<br />type DefaultParameterValueAttribute =<br />  inherit Attribute<br />  new : value:obj -> DefaultParameterValueAttribute<br />  member Value : obj<br /><br />--------------------<br />DefaultParameterValueAttribute(value: obj) : DefaultParameterValueAttribute</div>
<div class="tip" id="fs202">val lock : lockObject:'Lock -> action:(unit -> 'T) -> 'T (requires reference type)</div>
<div class="tip" id="fs203">val isNull : value:'T -> bool (requires 'T : null)</div>
<div class="tip" id="fs204">type IDisposable =<br />  member Dispose : unit -> unit</div>
<div class="tip" id="fs205">Multiple items<br />val seq : sequence:seq<'T> -> seq<'T><br /><br />--------------------<br />type seq<'T> = Collections.Generic.IEnumerable<'T></div>
<div class="tip" id="fs206">Multiple items<br />val float : value:'T -> float (requires member op_Explicit)<br /><br />--------------------<br />type float = Double<br /><br />--------------------<br />type float<'Measure> = float</div>
<div class="tip" id="fs207">Multiple items<br />module Result<br /><br />from IRT<br /><br />--------------------<br />module Result<br /><br />from Microsoft.FSharp.Core<br /><br />--------------------<br />[<Struct>]<br />type Result<'T,'TError> =<br />  | Ok of ResultValue: 'T<br />  | Error of ErrorValue: 'TError</div>
<div class="tip" id="fs208">val map : mapping:('T -> 'U) -> result:Result<'T,'TError> -> Result<'U,'TError></div>
<div class="tip" id="fs209">val rev : list:'T list -> 'T list</div>
<div class="tip" id="fs210">val mapError : mapping:('TError -> 'U) -> result:Result<'T,'TError> -> Result<'T,'U></div>
<div class="tip" id="fs211">String.Join(separator: string, values: Collections.Generic.IEnumerable<string>) : string<br />String.Join<'T>(separator: string, values: Collections.Generic.IEnumerable<'T>) : string<br />String.Join(separator: string, [<ParamArray>] values: obj []) : string<br />String.Join(separator: string, [<ParamArray>] value: string []) : string<br />String.Join(separator: string, value: string [], startIndex: int, count: int) : string</div>
<div class="tip" id="fs212">type IFormatProvider =<br />  member GetFormat : formatType:Type -> obj</div>
<div class="tip" id="fs213">field NumberStyles.Any: NumberStyles = 511</div>
<div class="tip" id="fs214">Multiple items<br />type CultureInfo =<br />  new : name:string -> CultureInfo + 3 overloads<br />  member Calendar : Calendar<br />  member ClearCachedData : unit -> unit<br />  member Clone : unit -> obj<br />  member CompareInfo : CompareInfo<br />  member CultureTypes : CultureTypes<br />  member DateTimeFormat : DateTimeFormatInfo with get, set<br />  member DisplayName : string<br />  member EnglishName : string<br />  member Equals : value:obj -> bool<br />  ...<br /><br />--------------------<br />CultureInfo(name: string) : CultureInfo<br />CultureInfo(culture: int) : CultureInfo<br />CultureInfo(name: string, useUserOverride: bool) : CultureInfo<br />CultureInfo(culture: int, useUserOverride: bool) : CultureInfo</div>
<div class="tip" id="fs215">property CultureInfo.InvariantCulture: CultureInfo</div>
<div class="tip" id="fs216">static member PCG.TryParse : s:string -> PCG option</div>
<div class="tip" id="fs217">val append : array1:'T [] -> array2:'T [] -> 'T []</div>
<div class="tip" id="fs218">val tryFind : predicate:('T -> bool) -> list:'T list -> 'T option</div>
<div class="tip" id="fs219">Multiple items<br />type StringBuilder =<br />  new : unit -> StringBuilder + 5 overloads<br />  member Append : value:string -> StringBuilder + 19 overloads<br />  member AppendFormat : format:string * arg0:obj -> StringBuilder + 7 overloads<br />  member AppendLine : unit -> StringBuilder + 1 overload<br />  member Capacity : int with get, set<br />  member Chars : int -> char with get, set<br />  member Clear : unit -> StringBuilder<br />  member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit<br />  member EnsureCapacity : capacity:int -> int<br />  member Equals : sb:StringBuilder -> bool<br />  ...<br /><br />--------------------<br />Text.StringBuilder() : Text.StringBuilder<br />Text.StringBuilder(capacity: int) : Text.StringBuilder<br />Text.StringBuilder(value: string) : Text.StringBuilder<br />Text.StringBuilder(value: string, capacity: int) : Text.StringBuilder<br />Text.StringBuilder(capacity: int, maxCapacity: int) : Text.StringBuilder<br />Text.StringBuilder(value: string, startIndex: int, length: int, capacity: int) : Text.StringBuilder</div>
<div class="tip" id="fs220">val ignore : value:'T -> unit</div>
<div class="tip" id="fs221">module Seq<br /><br />from Microsoft.FSharp.Collections</div>
<div class="tip" id="fs222">val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U></div>
<div class="tip" id="fs223">val max : source:seq<'T> -> 'T (requires comparison)</div>
<div class="tip" id="fs224">val append : source1:seq<'T> -> source2:seq<'T> -> seq<'T></div>
<div class="tip" id="fs225">val iter : action:('T -> unit) -> source:seq<'T> -> unit</div>
<div class="tip" id="fs226">val countBy : projection:('T -> 'Key) -> source:seq<'T> -> seq<'Key * int> (requires equality)</div>
<div class="tip" id="fs227">val choose : chooser:('T -> 'U option) -> source:seq<'T> -> seq<'U></div>
<div class="tip" id="fs228">val toList : source:seq<'T> -> 'T list</div>
<div class="tip" id="fs229">val collect : mapping:('T -> 'U list) -> list:'T list -> 'U list</div>
<div class="tip" id="fs230">val isEmpty : list:'T list -> bool</div>
<div class="tip" id="fs231">val where : predicate:('T -> bool) -> list:'T list -> 'T list</div>
<div class="tip" id="fs232">val distinctBy : projection:('T -> 'Key) -> list:'T list -> 'T list (requires equality)</div>
<div class="tip" id="fs233">val toArray : list:'T list -> 'T []</div>
<div class="tip" id="fs234">val unzip : array:('T1 * 'T2) [] -> 'T1 [] * 'T2 []</div>
<div class="tip" id="fs235">val contains : value:'T -> source:'T list -> bool (requires equality)</div>
<div class="tip" id="fs236">val choose : chooser:('T -> 'U option) -> list:'T list -> 'U list</div>
<div class="tip" id="fs237">val sortInPlace : array:'T [] -> unit (requires comparison)</div>
<div class="tip" id="fs238">val sqrt : value:'T -> 'U (requires member Sqrt)</div>
<div class="tip" id="fs239">val reduce : reduction:('T -> 'T -> 'T) -> list:'T list -> 'T</div>
<div class="tip" id="fs240">type Console =<br />  static member BackgroundColor : ConsoleColor with get, set<br />  static member Beep : unit -> unit + 1 overload<br />  static member BufferHeight : int with get, set<br />  static member BufferWidth : int with get, set<br />  static member CapsLock : bool<br />  static member Clear : unit -> unit<br />  static member CursorLeft : int with get, set<br />  static member CursorSize : int with get, set<br />  static member CursorTop : int with get, set<br />  static member CursorVisible : bool with get, set<br />  ...</div>
<div class="tip" id="fs241">Console.WriteLine() : unit<br />   <em>(+0 other overloads)</em><br />Console.WriteLine(value: string) : unit<br />   <em>(+0 other overloads)</em><br />Console.WriteLine(value: obj) : unit<br />   <em>(+0 other overloads)</em><br />Console.WriteLine(value: uint64) : unit<br />   <em>(+0 other overloads)</em><br />Console.WriteLine(value: int64) : unit<br />   <em>(+0 other overloads)</em><br />Console.WriteLine(value: uint32) : unit<br />   <em>(+0 other overloads)</em><br />Console.WriteLine(value: int) : unit<br />   <em>(+0 other overloads)</em><br />Console.WriteLine(value: float32) : unit<br />   <em>(+0 other overloads)</em><br />Console.WriteLine(value: float) : unit<br />   <em>(+0 other overloads)</em><br />Console.WriteLine(value: decimal) : unit<br />   <em>(+0 other overloads)</em></div>
<div class="tip" id="fs242">event Console.CancelKeyPress: IEvent<ConsoleCancelEventHandler,ConsoleCancelEventArgs></div>
<div class="tip" id="fs243">Multiple items<br />module Event<br /><br />from Microsoft.FSharp.Control<br /><br />--------------------<br />type Event<'T> =<br />  new : unit -> Event<'T><br />  member Trigger : arg:'T -> unit<br />  member Publish : IEvent<'T><br /><br />--------------------<br />type Event<'Delegate,'Args (requires delegate and 'Delegate :> Delegate)> =<br />  new : unit -> Event<'Delegate,'Args><br />  member Trigger : sender:obj * args:'Args -> unit<br />  member Publish : IEvent<'Delegate,'Args><br /><br />--------------------<br />new : unit -> Event<'T><br /><br />--------------------<br />new : unit -> Event<'Delegate,'Args></div>
<div class="tip" id="fs244">val add : callback:('T -> unit) -> sourceEvent:IEvent<'Del,'T> -> unit (requires delegate and 'Del :> Delegate)</div>
<div class="tip" id="fs245">Console.Write(value: string) : unit<br />   <em>(+0 other overloads)</em><br />Console.Write(value: obj) : unit<br />   <em>(+0 other overloads)</em><br />Console.Write(value: uint64) : unit<br />   <em>(+0 other overloads)</em><br />Console.Write(value: int64) : unit<br />   <em>(+0 other overloads)</em><br />Console.Write(value: uint32) : unit<br />   <em>(+0 other overloads)</em><br />Console.Write(value: int) : unit<br />   <em>(+0 other overloads)</em><br />Console.Write(value: float32) : unit<br />   <em>(+0 other overloads)</em><br />Console.Write(value: decimal) : unit<br />   <em>(+0 other overloads)</em><br />Console.Write(value: float) : unit<br />   <em>(+0 other overloads)</em><br />Console.Write(buffer: char []) : unit<br />   <em>(+0 other overloads)</em></div>
<div class="tip" id="fs246">val length : list:'T list -> int</div>
<div class="tip" id="fs247">Multiple items<br />val ref : value:'T -> 'T ref<br /><br />--------------------<br />type 'T ref = Ref<'T></div>
<div class="tip" id="fs248">type Int64 =<br />  struct<br />    member CompareTo : value:obj -> int + 1 overload<br />    member Equals : obj:obj -> bool + 1 overload<br />    member GetHashCode : unit -> int<br />    member GetTypeCode : unit -> TypeCode<br />    member ToString : unit -> string + 3 overloads<br />    static val MaxValue : int64<br />    static val MinValue : int64<br />    static member Parse : s:string -> int64 + 3 overloads<br />    static member TryParse : s:string * result:int64 -> bool + 1 overload<br />  end</div>
<div class="tip" id="fs249">field int64.MaxValue: int64 = 9223372036854775807L</div>
<div class="tip" id="fs250">type Interlocked =<br />  static member Add : location1:int * value:int -> int + 1 overload<br />  static member CompareExchange : location1:int * value:int * comparand:int -> int + 6 overloads<br />  static member Decrement : location:int -> int + 1 overload<br />  static member Exchange : location1:int * value:int -> int + 6 overloads<br />  static member Increment : location:int -> int + 1 overload<br />  static member MemoryBarrier : unit -> unit<br />  static member Read : location:int64 -> int64</div>
<div class="tip" id="fs251">Interlocked.Increment(location: byref<int64>) : int64<br />Interlocked.Increment(location: byref<int>) : int</div>
<div class="tip" id="fs252">type ThreadPool =<br />  static member BindHandle : osHandle:nativeint -> bool + 1 overload<br />  static member GetAvailableThreads : workerThreads:int * completionPortThreads:int -> unit<br />  static member GetMaxThreads : workerThreads:int * completionPortThreads:int -> unit<br />  static member GetMinThreads : workerThreads:int * completionPortThreads:int -> unit<br />  static member QueueUserWorkItem : callBack:WaitCallback -> bool + 1 overload<br />  static member RegisterWaitForSingleObject : waitObject:WaitHandle * callBack:WaitOrTimerCallback * state:obj * millisecondsTimeOutInterval:uint32 * executeOnlyOnce:bool -> RegisteredWaitHandle + 3 overloads<br />  static member SetMaxThreads : workerThreads:int * completionPortThreads:int -> bool<br />  static member SetMinThreads : workerThreads:int * completionPortThreads:int -> bool<br />  static member UnsafeQueueNativeOverlapped : overlapped:NativeOverlapped -> bool<br />  static member UnsafeQueueUserWorkItem : callBack:WaitCallback * state:obj -> bool<br />  ...</div>
<div class="tip" id="fs253">ThreadPool.UnsafeQueueUserWorkItem(callBack: WaitCallback, state: obj) : bool</div>
<div class="tip" id="fs254">module Option<br /><br />from Microsoft.FSharp.Core</div>
<div class="tip" id="fs255">val isNone : option:'T option -> bool</div>
<div class="tip" id="fs256">val set : array:'T [] -> index:int -> value:'T -> unit</div>
<div class="tip" id="fs257">val tryPick : chooser:('T -> 'U option) -> list:'T list -> 'U option</div>
<div class="tip" id="fs258">val pick : chooser:('T -> 'U option) -> list:'T list -> 'U</div>
<div class="tip" id="fs259">val defaultValue : value:'T -> option:'T option -> 'T</div>
<div class="tip" id="fs260">field Stopwatch.Frequency: int64</div>
<div class="tip" id="fs261">val defaultWith : defThunk:(unit -> 'T) -> option:'T option -> 'T</div>
<div class="tip" id="fs262">type Environment =<br />  static member CommandLine : string<br />  static member CurrentDirectory : string with get, set<br />  static member CurrentManagedThreadId : int<br />  static member Exit : exitCode:int -> unit<br />  static member ExitCode : int with get, set<br />  static member ExpandEnvironmentVariables : name:string -> string<br />  static member FailFast : message:string -> unit + 1 overload<br />  static member GetCommandLineArgs : unit -> string[]<br />  static member GetEnvironmentVariable : variable:string -> string + 1 overload<br />  static member GetEnvironmentVariables : unit -> IDictionary + 1 overload<br />  ...<br />  nested type SpecialFolder<br />  nested type SpecialFolderOption</div>
<div class="tip" id="fs263">Interlocked.Decrement(location: byref<int64>) : int64<br />Interlocked.Decrement(location: byref<int>) : int</div>
<div class="tip" id="fs264">val min : e1:'T -> e2:'T -> 'T (requires comparison)</div>
<div class="tip" id="fs265">val iteri : action:(int -> 'T -> unit) -> array:'T [] -> unit</div>
<div class="tip" id="fs266">val iter : action:('T -> unit) -> option:'T option -> unit</div>
<div class="tip" id="fs267">type GC =<br />  static member AddMemoryPressure : bytesAllocated:int64 -> unit<br />  static member CancelFullGCNotification : unit -> unit<br />  static member Collect : unit -> unit + 4 overloads<br />  static member CollectionCount : generation:int -> int<br />  static member EndNoGCRegion : unit -> unit<br />  static member GetGeneration : obj:obj -> int + 1 overload<br />  static member GetTotalMemory : forceFullCollection:bool -> int64<br />  static member KeepAlive : obj:obj -> unit<br />  static member MaxGeneration : int<br />  static member ReRegisterForFinalize : obj:obj -> unit<br />  ...</div>
<div class="tip" id="fs268">GC.GetTotalMemory(forceFullCollection: bool) : int64</div>
<div class="tip" id="fs269">Multiple items<br />type Thread =<br />  inherit CriticalFinalizerObject<br />  new : start:ThreadStart -> Thread + 3 overloads<br />  member Abort : unit -> unit + 1 overload<br />  member ApartmentState : ApartmentState with get, set<br />  member CurrentCulture : CultureInfo with get, set<br />  member CurrentUICulture : CultureInfo with get, set<br />  member DisableComObjectEagerCleanup : unit -> unit<br />  member ExecutionContext : ExecutionContext<br />  member GetApartmentState : unit -> ApartmentState<br />  member GetCompressedStack : unit -> CompressedStack<br />  member GetHashCode : unit -> int<br />  ...<br /><br />--------------------<br />Thread(start: ThreadStart) : Thread<br />Thread(start: ParameterizedThreadStart) : Thread<br />Thread(start: ThreadStart, maxStackSize: int) : Thread<br />Thread(start: ParameterizedThreadStart, maxStackSize: int) : Thread</div>
<div class="tip" id="fs270">Thread.Sleep(timeout: TimeSpan) : unit<br />Thread.Sleep(millisecondsTimeout: int) : unit</div>
<div class="tip" id="fs271">val max : e1:'T -> e2:'T -> 'T (requires comparison)</div>
<div class="tip" id="fs272">val iter : action:('T -> unit) -> list:'T list -> unit</div>
<div class="tip" id="fs273">Multiple items<br />val byte : value:'T -> byte (requires member op_Explicit)<br /><br />--------------------<br />type byte = Byte</div>
<div class="tip" id="fs274">val defaultArg : arg:'T option -> defaultValue:'T -> 'T</div>
<div class="tip" id="fs275">field int.MaxValue: int = 2147483647</div>
<div class="tip" id="fs276">type BitConverter =<br />  static val IsLittleEndian : bool<br />  static member DoubleToInt64Bits : value:float -> int64<br />  static member GetBytes : value:bool -> byte[] + 9 overloads<br />  static member Int64BitsToDouble : value:int64 -> float<br />  static member ToBoolean : value:byte[] * startIndex:int -> bool<br />  static member ToChar : value:byte[] * startIndex:int -> char<br />  static member ToDouble : value:byte[] * startIndex:int -> float<br />  static member ToInt16 : value:byte[] * startIndex:int -> int16<br />  static member ToInt32 : value:byte[] * startIndex:int -> int<br />  static member ToInt64 : value:byte[] * startIndex:int -> int64<br />  ...</div>
<div class="tip" id="fs277">BitConverter.Int64BitsToDouble(value: int64) : float</div>
<div class="tip" id="fs278">type 'T array = 'T []</div>
<div class="tip" id="fs279">val toList : array:'T [] -> 'T list</div>
<div class="tip" id="fs280">val unzip : list:('T1 * 'T2) list -> 'T1 list * 'T2 list</div>
<div class="tip" id="fs281">val init : count:int -> initializer:(int -> 'T) -> seq<'T></div>
<div class="tip" id="fs282">Multiple items<br />val char : value:'T -> char (requires member op_Explicit)<br /><br />--------------------<br />type char = Char</div>
<div class="tip" id="fs283">module Operators<br /><br />from Microsoft.FSharp.Core</div>
<div class="tip" id="fs284">val int : value:'T -> int (requires member op_Explicit)</div>
<div class="tip" id="fs285">val uint64 : value:'T -> uint64 (requires member op_Explicit)</div>
<div class="tip" id="fs286">Multiple items<br />type CompilationRepresentationAttribute =<br />  inherit Attribute<br />  new : flags:CompilationRepresentationFlags -> CompilationRepresentationAttribute<br />  member Flags : CompilationRepresentationFlags<br /><br />--------------------<br />new : flags:CompilationRepresentationFlags -> CompilationRepresentationAttribute</div>
<div class="tip" id="fs287">type CompilationRepresentationFlags =<br />  | None = 0<br />  | Static = 1<br />  | Instance = 2<br />  | ModuleSuffix = 4<br />  | UseNullAsTrueValue = 8<br />  | Event = 16</div>
<div class="tip" id="fs288">CompilationRepresentationFlags.ModuleSuffix: CompilationRepresentationFlags = 4</div>
<div class="tip" id="fs289">module Printf<br /><br />from Microsoft.FSharp.Core</div>
<div class="tip" id="fs290">val kbprintf : continuation:(unit -> 'Result) -> builder:Text.StringBuilder -> format:Printf.BuilderFormat<'T,'Result> -> 'T</div>
<div class="tip" id="fs291">val box : value:'T -> obj</div>
<div class="tip" id="fs292">val exists : predicate:('T -> bool) -> array:'T [] -> bool</div>
<div class="tip" id="fs293">val fold2 : folder:('State -> 'T1 -> 'T2 -> 'State) -> state:'State -> array1:'T1 [] -> array2:'T2 [] -> 'State</div>
<div class="tip" id="fs294">val abs : value:'T -> 'T (requires member Abs)</div>
<div class="tip" id="fs295">val get : option:'T option -> 'T</div>
<div class="tip" id="fs296">val create : count:int -> value:'T -> 'T []</div>
<div class="tip" id="fs297">Multiple items<br />type Version =<br />  new : unit -> Version + 4 overloads<br />  member Build : int<br />  member Clone : unit -> obj<br />  member CompareTo : version:obj -> int + 1 overload<br />  member Equals : obj:obj -> bool + 1 overload<br />  member GetHashCode : unit -> int<br />  member Major : int<br />  member MajorRevision : int16<br />  member Minor : int<br />  member MinorRevision : int16<br />  ...<br /><br />--------------------<br />Version() : Version<br />Version(version: string) : Version<br />Version(major: int, minor: int) : Version<br />Version(major: int, minor: int, build: int) : Version<br />Version(major: int, minor: int, build: int, revision: int) : Version</div>
<div class="tip" id="fs298">val defaultValueArg : arg:'T voption -> defaultValue:'T -> 'T</div>
<div class="tip" id="fs299">val dict : keyValuePairs:seq<'Key * 'Value> -> Collections.Generic.IDictionary<'Key,'Value> (requires equality)</div>
<div class="tip" id="fs300">val mapi : mapping:(int -> 'T -> 'U) -> array:'T [] -> 'U []</div>
<div class="tip" id="fs301">Multiple items<br />module Map<br /><br />from Microsoft.FSharp.Collections<br /><br />--------------------<br />type Map<'Key,'Value (requires comparison)> =<br />  interface IReadOnlyDictionary<'Key,'Value><br />  interface IReadOnlyCollection<KeyValuePair<'Key,'Value>><br />  interface IEnumerable<br />  interface IComparable<br />  interface IEnumerable<KeyValuePair<'Key,'Value>><br />  interface ICollection<KeyValuePair<'Key,'Value>><br />  interface IDictionary<'Key,'Value><br />  new : elements:seq<'Key * 'Value> -> Map<'Key,'Value><br />  member Add : key:'Key * value:'Value -> Map<'Key,'Value><br />  member ContainsKey : key:'Key -> bool<br />  ...<br /><br />--------------------<br />new : elements:seq<'Key * 'Value> -> Map<'Key,'Value></div>
<div class="tip" id="fs302">val add : key:'Key -> value:'T -> table:Map<'Key,'T> -> Map<'Key,'T> (requires comparison)</div>
<div class="tip" id="fs303">val empty<'Key,'T (requires comparison)> : Map<'Key,'T> (requires comparison)</div>
<div class="tip" id="fs304">val tryFind : key:'Key -> table:Map<'Key,'T> -> 'T option (requires comparison)</div>
Causal Profiling in .NET
2019-10-11T00:00:00+01:00
http://anthonylloyd.github.io/blog/2019/10/11/causal-profiling
<p>Recently there was an interesting talk by Emery Berger on a multithreading profiling technique called causal profiling.
The idea is that by slowing everything else down running concurrently with a region of code, you can infer what the effect would be of speeding up that code.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/r-TLSBdHe1A" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<p>The talk covers a C++ library called Coz. This post explores if this can be done in .NET.</p>
<p>To achieve this, code to define the start and end of each region will have to be added to the codebase directly.
This fits well with using a debug library in preference to step through debugging discussed in a previous <a href="/blog/2017/04/30/kicking-the-debugger">post</a>.</p>
<h2><a name="Simple-implementation" class="anchor" href="#Simple-implementation">Simple implementation</a></h2>
<p>There is a simpler way that could achieve the same aim.
Like many physical systems there is a scale invariance in multithreaded systems.
If all regions are proportionally slowed down, then the causal threading picture will not change. It is just like enlarging a photo.</p>
<p>So, if all regions apart from one are proportionally slowed down, and the run time is compared with a run where everything is slowed down, this virtual speed up can be deduced.</p>
<p>This can be implemented by simply recording the region start and end time and spinning in the end region call for a given percentage of the region time span.
Spinning is necessary rather than sleeping as it needs to simulate work and not encourage a context switch.</p>
<img src="/public/perf/CausalSimple.png" title="PerfSimple.fs"/>
<p>The code can be found <a href="https://github.com/AnthonyLloyd/Causal/blob/master/dbg/PerfSimple.fs">here</a>.</p>
<h2><a name="Full-implementation" class="anchor" href="#Full-implementation">Full implementation</a></h2>
<p>The full implementation is more involved due to two main issues.</p>
<p>Firstly, the slow down for regions will depend on measurements of the overlap with some other region.
This region may be being run on multiple threads and the slow down should not be double counted.
It is the overlap with one or more.</p>
<p>Secondly, this interesting bookkeeping will inevitably lead to efficient locking code being needed.
<code>Interlocked</code> low locking will not work as there are multiple variables to track (region thread count, region on since and total delay).
This is going to need <code>SpinLock</code> (again to discourage a context switch) and as little code as possible.</p>
<img src="/public/perf/CausalFull.png" title="Perf.fs"/>
<p>The code can be found <a href="https://github.com/AnthonyLloyd/Causal/blob/master/dbg/Perf.fs">here</a>.</p>
<h2><a name="Statistics" class="anchor" href="#Statistics">Statistics</a></h2>
<p>These measurements need to be run for an array of delay percentages for each region defined.
This defines an iteration.
This is then repeated, and the results are summarised after each iteration.</p>
<p>The summary statistics are the median and standard error after outliers are removed.
Outliers are defined as measurements outside of 3 times MAD as described in a previous <a href="/blog/2016/10/21/MAD-Outliers">post</a>.</p>
<p>Below is the output of the <a href="https://github.com/AnthonyLloyd/Causal">repo</a> Fasta example:</p>
<img src="/public/perf/CausalProfiling.png" title="Casual Profiling"/>
<p>The summary table shows:</p>
<ul>
<li>Region - the region name defined in <code>regionStart</code>.</li>
<li>Count - the number of times the region code is called in each run.</li>
<li>Time% - the total time in the region divided by the total elapsed time divided by the number of cores. This is only a small sample approximation.</li>
<li>+n% - median and error program time % change when the region itself is slowed down by a given %.</li>
<li>-n% - median and error inferred program time % change when other regions are slowed down by a given % to model a virtual speed up of this region.</li>
</ul>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p>The results for the simple and full implementation are within the error margin for the Fasta example.
The full implementation due to how it is calculated has a smaller error for the same number of iterations.
The full implementation is probably what will be used going forward but it is good to keep the simple version for comparison.</p>
<p>The talk above discusses how this technique can be extended to profile throughput and latency.
This would be a simple extension to the existing implementation.</p>
<p>It's amazing what can be achieved with a good idea (stolen!), some statistics and less than 200 lines of code.
This technique and <a href="https://github.com/haf/expecto">Expecto's</a> <code>isFasterThan</code> created from a previous <a href="/blog/2016/05/20/performance-testing">post</a>
are examples of how a small amount of code can produce fast, simple and statistically robust performance tools.</p>
Fsion - 1. Size
2019-05-17T00:00:00+01:00
http://anthonylloyd.github.io/blog/2019/05/17/fsion-01-size
<p>PLEASE NOTE SOURCE CODE IS NO LONGER AVAILABLE AND THE POST IS HERE JUST FOR INFORMATION</p>
<p>Previously we introduced <a href="/blog/2019/05/17/fsion-00-introduction">Fsion - 0. Introduction</a>, now we will explore database size.</p>
<p>The sample data set is around 10 months of daily position files for the 280
<a href="https://www.ishares.com/uk/intermediaries/en/products/etf-product-list#!type=emeaIshares&tab=overview&view=list">iShares</a>
funds available online.
All fields in the files are loaded apart from any that can be derived.</p>
<table>
<tbody>
<tr class="odd">
<td><p><strong>Funds</strong></p></td>
<td><p>280</p></td>
</tr>
<tr class="even">
<td><p><strong>Days</strong></p></td>
<td><p>206</p></td>
</tr>
<tr class="odd">
<td><p><strong>Position files</strong></p></td>
<td><p>71,261</p></td>
</tr>
<tr class="even">
<td><p><strong>Size unzipped</strong></p></td>
<td><p>4.4 GB</p></td>
</tr>
<tr class="odd">
<td><p><strong>Size .zip normal</strong></p></td>
<td><p>1.1 GB</p></td>
</tr>
<tr class="even">
<td><p><strong>Size .7z ultra</strong></p></td>
<td><p>199 MB</p></td>
</tr>
</tbody>
</table>
<h2><a name="DataSeries-Compression" class="anchor" href="#DataSeries-Compression">DataSeries Compression</a></h2>
<p>DataSeries is an immutable collection representing an ordered table of Reporting Date, Transaction Id and <code>int64</code> encoded Values
with the latest values at the top.</p>
<p>This is encoded as a byte array.
The first row is stored as <a href="https://developers.google.com/protocol-buffers/docs/encoding">varints</a>.
Each subsequent row is stored as a difference to the above field values as <a href="https://developers.google.com/protocol-buffers/docs/encoding">varints</a>.</p>
<p>With sensible encoding using offsets and the fact that values tend to be close to zero, even single row DataSeries
are several times smaller than a more standard representation.
Since the table is ordered, and the values in each row are very likely to be close to the
ones above, very high compression ratios are <a href="https://github.com/Genbox/CSharpFastPFOR">possible</a>.</p>
<h2><a name="Data-Details" class="anchor" href="#Data-Details">Data Details</a></h2>
<p>Text values are stored in a <code>SetSlim<Text></code> collection, numeric values are encoded directly to <code>int64</code>.
The DataSeries are stored in a <code>MapSlim<EntityAttribute,DataSeries></code>.</p>
<p>Below is a table of count and number of bytes by entity type (column) and attribute (row):</p>
<p>Text: Count = 59,099 Max length = 50</p>
<table>
<thead>
<tr class="header">
<th align="center"><p>Count<br/>Bytes</p></th>
<th align="center"><p>transaction</p></th>
<th align="center"><p>entitytype</p></th>
<th align="center"><p>attribute</p></th>
<th align="center"><p>instrument</p></th>
<th align="center"><p>position</p></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="center"><p>uri</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
</tr>
<tr class="even">
<td align="center"><p>name</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>5<br/>15</p></td>
<td align="center"><p>20<br/>60</p></td>
<td align="center"><p>38,036<br/>279,391</p></td>
<td align="center"><p>0<br/>0</p></td>
</tr>
<tr class="odd">
<td align="center"><p>time</p></td>
<td align="center"><p>71,262<br/>783,876</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
</tr>
<tr class="even">
<td align="center"><p>attribute_type</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>20<br/>60</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
</tr>
<tr class="odd">
<td align="center"><p>attribute_isset</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
</tr>
<tr class="even">
<td align="center"><p>isin</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>36,476<br/>273,162</p></td>
<td align="center"><p>0<br/>0</p></td>
</tr>
<tr class="odd">
<td align="center"><p>ticker</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>38,036<br/>275,050</p></td>
<td align="center"><p>0<br/>0</p></td>
</tr>
<tr class="even">
<td align="center"><p>currency</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>38,036<br/>240,802</p></td>
<td align="center"><p>0<br/>0</p></td>
</tr>
<tr class="odd">
<td align="center"><p>assetclass</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>38,023<br/>261,032</p></td>
<td align="center"><p>0<br/>0</p></td>
</tr>
<tr class="even">
<td align="center"><p>sector</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>37,927<br/>258,075</p></td>
<td align="center"><p>0<br/>0</p></td>
</tr>
<tr class="odd">
<td align="center"><p>exchange</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>12,046<br/>79,467</p></td>
<td align="center"><p>0<br/>0</p></td>
</tr>
<tr class="even">
<td align="center"><p>country</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>38,036<br/>267,549</p></td>
<td align="center"><p>0<br/>0</p></td>
</tr>
<tr class="odd">
<td align="center"><p>coupon</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>25,552<br/>193,815</p></td>
<td align="center"><p>0<br/>0</p></td>
</tr>
<tr class="even">
<td align="center"><p>maturity</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>25,546<br/>205,849</p></td>
<td align="center"><p>0<br/>0</p></td>
</tr>
<tr class="odd">
<td align="center"><p>price</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>38,036<br/>15,846,249</p></td>
<td align="center"><p>0<br/>0</p></td>
</tr>
<tr class="even">
<td align="center"><p>duration</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>25,552<br/>5,740,430</p></td>
<td align="center"><p>0<br/>0</p></td>
</tr>
<tr class="odd">
<td align="center"><p>fund</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>147,323<br/>1,184,564</p></td>
</tr>
<tr class="even">
<td align="center"><p>instrument</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>147,323<br/>1,096,271</p></td>
</tr>
<tr class="odd">
<td align="center"><p>nominal</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>0<br/>0</p></td>
<td align="center"><p>147,323<br/>8,306,493</p></td>
</tr>
</tbody>
</table>
<h2><a name="Size-Estimates" class="anchor" href="#Size-Estimates">Size Estimates</a></h2>
<p>Memory 32-bit: Text = 2.9 MB Data = 61.3 MB Total = 64.2 MB<br />
Memory 64-bit: Text = 3.8 MB Data = 75.1 MB Total = 78.9 MB<br />
Size on disk = 40.3 MB</p>
<img src="/public/fsion/size-by-files.png" title="size by files" />
<p>Extrapolating these curves to 10 years of files would give total memory usage of around 650 MB.
The files contain the key changing attributes.
A database with a number of additional attributes would be expected to comfortably fit in 1 to 5 GB.</p>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p>The database file size is small enough to <a href="https://github.com/AnthonyLloyd/Fsion/blob/master/data/2019-05-13_210216.3290164.fsp">store</a> in github and can be used going forward for testing and performance benchmarking.</p>
<p>Looking at the size on disk compared to 32-bit and 64-bit in memory estimates shows that the objects and pointers contribute a large amount to the size.
This is <a href="https://www.red-gate.com/simple-talk/dotnet/.net-framework/object-overhead-the-hidden-.net-memory--allocation-cost/">not surprising</a> since each 32-bit object has an 8 byte header and 16 bytes for 64-bit, plus 4 and 8 bytes for each reference respectively.
A whole single row DataSeries in the above table is only around 8 bytes.</p>
<p>If the DataSeries were not in a time series compressed format this object and pointer overhead would be a lot higher.
This agrees with what is often found in server caches. Holding and tracking fine grained subsets of the database can actually use a lot of memory.</p>
<p>The data modelled is for one of the largest financial asset managers.
Fast to calculate derived data such as profit and returns (which also tend to be costly to store) should be excluded.
By doing this and storing only transaction data compressed in memory it is possible to use Fsion for many financial databases.</p>
<p>It also shows the estimates in the <a href="/blog/2018/02/01/architecture-data-first">Data-First Architecture</a> post are too
high as they don't take account of the DataSeries compression that is possible.</p>
<p>Next, we will look at the performance characteristics of this database compared to other options.</p>
Fsion - 0. Introduction
2019-05-17T00:00:00+01:00
http://anthonylloyd.github.io/blog/2019/05/17/fsion-00-introduction
<p>PLEASE NOTE SOURCE CODE IS NO LONGER AVAILABLE AND THE POST IS HERE JUST FOR INFORMATION</p>
<p><a href="https://github.com/AnthonyLloyd/Fsion">Fsion</a> is an EAVT (Entity, Attribute, Value, Time) time-series database for F#.</p>
<p>The main idea is by storing value updates as compressed data series, an embedded in memory
<a href="https://www.networkworld.com/article/3186634/what-is-bitemporal-and-why-should-the-enterprise-care.html">bitemporal</a>
database can be created that is orders of magnitude smaller and faster than traditional
database solutions.
A functional in memory database can also be made more type-safe with a simpler API.
Other key ideas can be found <a href="https://github.com/AnthonyLloyd/Fsion#key-ideas">here</a>.</p>
<p><a href="https://github.com/AnthonyLloyd/Fsion">Fsion</a> has been designed with the finance domain in mind but could be equally applicable to
domains that requires a degree of time-series, historic query or audit functionality.</p>
<p>The following sections outline the main <a href="https://github.com/AnthonyLloyd/Fsion">Fsion</a> components.</p>
<h2><a name="SetSlim-and-MapSlim" class="anchor" href="#SetSlim-and-MapSlim">SetSlim and MapSlim</a></h2>
<p><a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion/SetSlim.fs">SetSlim.fs</a> / <a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion.Tests/SetSlimTests.fs">SetSlimTests.fs</a> / <a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion/MapSlim.fs">MapSlim.fs</a> / <a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion.Tests/MapSlimTests.fs">MapSlimTests.fs</a></p>
<p><code>SetSlim<T></code> and <code>MapSlim<K,V></code> are low memory, high performance replacements for the mutable
collections <code>HashSet<T></code> and <code>Dictionary<K,V></code>.
A previous <a href="/blog/2018/12/14/mapslim">post</a> covers their design and ~50% performance
improvement.</p>
<p>An important additional feature of these collections is that they do not need to be locked for
read while updates are being applied.
This combined with <a href="/blog/2019/03/29/io">IO</a> will hopefully result
in a completely lock-free multi-threaded read model for <a href="https://github.com/AnthonyLloyd/Fsion">Fsion</a>.</p>
<h2><a name="DataSeries" class="anchor" href="#DataSeries">DataSeries</a></h2>
<p><a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion/DataSeries.fs">DataSeries.fs</a> / <a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion.Tests/DataSeriesTests.fs">DataSeriesTests.fs</a></p>
<p>DataSeries is an immutable collection representing an ordered table of Reporting Date, Transaction Id and <code>int64</code> encoded
Values with the latest values at the top. This forms a <a href="https://www.networkworld.com/article/3186634/what-is-bitemporal-and-why-should-the-enterprise-care.html">bitemporal</a>
representation of each value in the database.</p>
<p>The next post <a href="/blog/2019/05/17/fsion-01-size">Fsion - 1. Size</a> demonstrates the
compression that can be achieved using this data structure.</p>
<p>DateSeries also naturally support sets with add and remove operations.
This is much easier than managing foreign keys sets in traditional databases and will come in
handy when automatic indexes are added.</p>
<h2><a name="Transactor" class="anchor" href="#Transactor">Transactor</a></h2>
<p><a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion/Transactor.fs">Transactor.fs</a> / <a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion.Tests/TransactorTests.fs">TransactorTests.fs</a></p>
<p>The Transactor is responsible for making concurrent transactions consistent before persisting
and notifying subscribed Selectors.</p>
<p>Transactions are created from a Selector Store with any new Entity Ids and Transaction Id
following on from the Stores Ids.
The Transactor if necessary (due to concurrent transactions) corrects these.
Any corrected transactions have <code>transaction_based_on</code> set to the original Transaction Id.
Other processes would need to resolve data conflicts based on required business logic.</p>
<p>Transactions are themselves first class Entities in the database so any context data such as
time, user, process or source can be added.</p>
<h2><a name="Selector" class="anchor" href="#Selector">Selector</a></h2>
<p><a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion/Selector.fs">Selector.fs</a> / <a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion.Tests/SelectorTests.fs">SelectorTests.fs</a></p>
<p>Selector is responsible for applying Transactions to a Store, saving and loading a snapshot
and has an API for selecting data.</p>
<p>The Selector takes the Transaction Id in all API functions and can be performed at any past
Transaction Id.
The same results will be returned for the same parameters any time in the future.
This means Selection API calls are pure.</p>
<p>Transaction Id represents a consistent point in time.
Long running processes can make several calls using the same Transaction Id.
Consistency of data in database or cache system design is often overlooked.</p>
<h2><a name="View" class="anchor" href="#View">View</a></h2>
<p><em>WIP</em></p>
<p>Views are functions that can be passed to the Selector API to make selection easier and
more type-safe.</p>
<p><code>Store -> Tx -> Entity -> Result<T,ValidationErrors></code></p>
<p>They can be thought of as both the schema definition (when run with an empty store) and
validation function.
Multiple views can be defined for the same Entity Type for different areas of the business
logic.</p>
<h2><a name="Why-F" class="anchor" href="#Why-F">Why F#</a></h2>
<ul>
<li>Type-Safe - native typed values and marshalling unstructured data into type-safe views.</li>
<li>Functional - pure, consistent and repeatable API calls returning immutable data.</li>
<li>Simple - simple code will allow us to add more sophisticated functionality.</li>
<li>Robust - in handling of errors and multi-threaded code.</li>
<li>Testing - property based testing for serialization, stress testing for threading.</li>
</ul>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p>Traditional databases are not efficient as highly normalised stores with full history.
A compressed in memory store is smaller, faster and offers simpler functionality with no 'mapping'.</p>
<p>The design is flexible and can scale with the Transactor and Selector as separate processes.
The Selector Store can be based on <a href="https://github.com/Microsoft/FASTER">FASTER</a> if the size of the
database when compressed is larger than memory.
Multiple Selector Stores can be run and filtered as a cache on the client.</p>
<p>The project continues to be work in progress.
Next steps are to make the API as type-safe as possible in terms of Attributes, Transactions
and Selection with schema being defined in code.
Work will also be done to make sure the API is resilient and performance optimised.
The roadmap can be found <a href="https://github.com/AnthonyLloyd/Fsion/#roadmap">here</a>.</p>
<p>Ultimately the aim is to provide a functional fully type-safe database and cache functionality
with a set of best practice meta data driven satellite projects.</p>
<p>Next, we will look at database size in <a href="/blog/2019/05/17/fsion-01-size">Fsion - 1. Size</a>.</p>
F# Implementation of Scala ZIO
2019-03-29T00:00:00+00:00
http://anthonylloyd.github.io/blog/2019/03/29/io
<p>PLEASE NOTE SOURCE CODE IS NO LONGER AVAILABLE AND THE POST IS HERE JUST FOR INFORMATION</p>
<p>This is a prototype implementation of <a href="https://github.com/scalaz/scalaz-zio">Scala ZIO</a> in F#.
It aims to be a skeleton of ZIO features such that additional functions can be easily fleshed out.</p>
<h2><a name="Background" class="anchor" href="#Background">Background</a></h2>
<p>I recently went to a <a href="https://www.slideshare.net/jdegoes/the-death-of-final-tagless">talk</a>
on <a href="https://github.com/scalaz/scalaz-zio">Scala ZIO</a> by <a href="https://twitter.com/jdegoes">John De Goes</a>.
ZIO is a type-safe, composable library for asynchronous and concurrent programming in Scala.</p>
<p>It takes a different approach to other Scala effects libraries in that it does not require the use of Higher-Kinded Types.
Instead it uses a reader monad to provide access to IO effects (called ZIO Environment).</p>
<p>I came away wanting something similar in F#.
A useful library that could be used in the outer IO layer to simplify and test IO dependency code.
I started to play with some reader code but didn't think it would ultimately work out.
In fact, it works really well.</p>
<h2><a name="IO" class="anchor" href="#IO">IO</a></h2>
<p><span class="math">\[IO = Reader + Async + Result\]</span></p>
<p>The F# equivalent of ZIO type aliases are <code>UIO<'r,'a></code> which represents effects without errors,
and <code>IO<'r,'a,'e></code> which represents effects with a possible error.
<a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion/IO.fs">IO</a> combines reader, async and result into one unified computation expression.</p>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="k">type</span> <span onmouseout="hideTip(event, 'fs37', 72)" onmouseover="showTip(event, 'fs37', 72)" class="rt">UIO</span><span class="pn"><</span><span class="ta">'</span><span class="id">r</span><span class="pn">,</span><span class="ta">'</span><span class="id">a</span><span class="pn">></span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs37', 73)" onmouseover="showTip(event, 'fs37', 73)" class="uc">UIO</span> <span class="k">of</span> <span class="pn">(</span><span class="ta">'</span><span class="id">r</span> <span class="pn">*</span> <span onmouseout="hideTip(event, 'fs38', 74)" onmouseover="showTip(event, 'fs38', 74)" class="rt">Cancel</span> <span class="k">-></span> <span class="pn">(</span><span class="ta">'</span><span class="id">a</span> <span onmouseout="hideTip(event, 'fs39', 75)" onmouseover="showTip(event, 'fs39', 75)" class="rt">option</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs40', 76)" onmouseover="showTip(event, 'fs40', 76)" class="rt">unit</span><span class="pn">)</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs40', 77)" onmouseover="showTip(event, 'fs40', 77)" class="rt">unit</span><span class="pn">)</span>
</code></pre>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="k">type</span> <span onmouseout="hideTip(event, 'fs130', 466)" onmouseover="showTip(event, 'fs130', 466)" class="rt">IO</span><span class="pn"><</span><span class="ta">'</span><span class="id">r</span><span class="pn">,</span><span class="ta">'</span><span class="id">a</span><span class="pn">,</span><span class="ta">'</span><span class="id">e</span><span class="pn">></span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs130', 467)" onmouseover="showTip(event, 'fs130', 467)" class="uc">IO</span> <span class="k">of</span> <span class="pn">(</span><span class="ta">'</span><span class="id">r</span> <span class="pn">*</span> <span onmouseout="hideTip(event, 'fs38', 468)" onmouseover="showTip(event, 'fs38', 468)" class="rt">Cancel</span> <span class="k">-></span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs131', 469)" onmouseover="showTip(event, 'fs131', 469)" class="rt">Result</span><span class="pn"><</span><span class="ta">'</span><span class="id">a</span><span class="pn">,</span><span class="ta">'</span><span class="id">e</span><span class="pn">></span> <span onmouseout="hideTip(event, 'fs39', 470)" onmouseover="showTip(event, 'fs39', 470)" class="rt">option</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs40', 471)" onmouseover="showTip(event, 'fs40', 471)" class="rt">unit</span><span class="pn">)</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs40', 472)" onmouseover="showTip(event, 'fs40', 472)" class="rt">unit</span><span class="pn">)</span>
</code></pre>
<h2><a name="Reader" class="anchor" href="#Reader">Reader</a></h2>
<p>The reader part represents all the environment dependencies required in the computation expression.
It is fully type-safe with types inferred including any library requirements such as Clock for the timeout.
The computation expression can easily be <a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion.Tests/IOTests.fs">tested</a> by running with a test environment.</p>
<img style="margin-left:20px" src="/public/io/programType.png" title="program type" />
<h2><a name="Async" class="anchor" href="#Async">Async</a></h2>
<p>At the IO layer thread pool threads need to be used in the most efficient way without any blocking.
This usually means Async in F# or async/await in C# need to be used.
They both join threads without a thread pool thread having to wait.</p>
<pre class="fssnip highlighted"><code lang="fsharp"> <span class="k">let</span> <span onmouseout="hideTip(event, 'fs200', 1127)" onmouseover="showTip(event, 'fs200', 1127)" class="fn">race</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs93', 1128)" onmouseover="showTip(event, 'fs93', 1128)" class="uc">UIO</span> <span onmouseout="hideTip(event, 'fs201', 1129)" onmouseover="showTip(event, 'fs201', 1129)" class="fn">run1</span><span class="pn">)</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs130', 1130)" onmouseover="showTip(event, 'fs130', 1130)" class="uc">IO</span> <span onmouseout="hideTip(event, 'fs202', 1131)" onmouseover="showTip(event, 'fs202', 1131)" class="fn">run2</span><span class="pn">)</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs130', 1132)" onmouseover="showTip(event, 'fs130', 1132)" class="rt">IO</span><span class="pn"><</span><span class="ta">'</span><span class="id">r</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs141', 1133)" onmouseover="showTip(event, 'fs141', 1133)" class="rt">Choice</span><span class="pn"><</span><span class="ta">'</span><span class="id">a1</span><span class="pn">,</span><span class="ta">'</span><span class="id">a2</span><span class="pn">></span><span class="pn">,</span><span class="ta">'</span><span class="id">e1</span><span class="pn">></span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs130', 1134)" onmouseover="showTip(event, 'fs130', 1134)" class="uc">IO</span> <span class="pn">(</span><span class="k">fun</span> <span onmouseout="hideTip(event, 'fs44', 1135)" onmouseover="showTip(event, 'fs44', 1135)" class="id">env</span> <span onmouseout="hideTip(event, 'fs203', 1136)" onmouseover="showTip(event, 'fs203', 1136)" class="fn">cont</span> <span class="k">-></span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs38', 1137)" onmouseover="showTip(event, 'fs38', 1137)" class="m">Cancel</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs24', 1138)" onmouseover="showTip(event, 'fs24', 1138)" class="id">isSet</span> <span onmouseout="hideTip(event, 'fs44', 1139)" onmouseover="showTip(event, 'fs44', 1139)" class="id">env</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs203', 1140)" onmouseover="showTip(event, 'fs203', 1140)" class="fn">cont</span> <span onmouseout="hideTip(event, 'fs46', 1141)" onmouseover="showTip(event, 'fs46', 1141)" class="uc">None</span>
<span class="k">else</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs190', 1142)" onmouseover="showTip(event, 'fs190', 1142)" class="id">envChild</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs38', 1143)" onmouseover="showTip(event, 'fs38', 1143)" class="m">Cancel</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 1144)" onmouseover="showTip(event, 'fs27', 1144)" class="id">add</span> <span onmouseout="hideTip(event, 'fs44', 1145)" onmouseover="showTip(event, 'fs44', 1145)" class="id">env</span>
<span class="k">let</span> <span class="k">mutable</span> <span onmouseout="hideTip(event, 'fs204', 1146)" onmouseover="showTip(event, 'fs204', 1146)" class="mv">o</span> <span class="o">=</span> <span class="n">0</span>
<span onmouseout="hideTip(event, 'fs81', 1147)" onmouseover="showTip(event, 'fs81', 1147)" class="rt">ThreadPool</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs82', 1148)" onmouseover="showTip(event, 'fs82', 1148)" class="id">QueueUserWorkItem</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">_</span> <span class="k">-></span>
<span onmouseout="hideTip(event, 'fs201', 1149)" onmouseover="showTip(event, 'fs201', 1149)" class="fn">run1</span> <span onmouseout="hideTip(event, 'fs190', 1150)" onmouseover="showTip(event, 'fs190', 1150)" class="id">envChild</span> <span class="pn">(</span><span class="k">fun</span> <span onmouseout="hideTip(event, 'fs205', 1151)" onmouseover="showTip(event, 'fs205', 1151)" class="id">a</span> <span class="k">-></span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs85', 1152)" onmouseover="showTip(event, 'fs85', 1152)" class="rt">Interlocked</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs199', 1153)" onmouseover="showTip(event, 'fs199', 1153)" class="id">Exchange</span><span class="pn">(</span><span class="o">&</span><span onmouseout="hideTip(event, 'fs204', 1154)" onmouseover="showTip(event, 'fs204', 1154)" class="mv">o</span><span class="pn">,</span><span class="n">1</span><span class="pn">)</span> <span class="o">=</span> <span class="n">0</span> <span class="k">then</span>
<span onmouseout="hideTip(event, 'fs38', 1155)" onmouseover="showTip(event, 'fs38', 1155)" class="m">Cancel</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs31', 1156)" onmouseover="showTip(event, 'fs31', 1156)" class="id">set</span> <span onmouseout="hideTip(event, 'fs190', 1157)" onmouseover="showTip(event, 'fs190', 1157)" class="id">envChild</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs38', 1158)" onmouseover="showTip(event, 'fs38', 1158)" class="m">Cancel</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs24', 1159)" onmouseover="showTip(event, 'fs24', 1159)" class="id">isSet</span> <span onmouseout="hideTip(event, 'fs44', 1160)" onmouseover="showTip(event, 'fs44', 1160)" class="id">env</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs203', 1161)" onmouseover="showTip(event, 'fs203', 1161)" class="fn">cont</span> <span onmouseout="hideTip(event, 'fs46', 1162)" onmouseover="showTip(event, 'fs46', 1162)" class="uc">None</span>
<span class="k">else</span> <span onmouseout="hideTip(event, 'fs48', 1163)" onmouseover="showTip(event, 'fs48', 1163)" class="m">Option</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs49', 1164)" onmouseover="showTip(event, 'fs49', 1164)" class="id">map</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs17', 1165)" onmouseover="showTip(event, 'fs17', 1165)" class="uc">Choice2Of2</span> <span class="o">></span><span class="pn">></span> <span onmouseout="hideTip(event, 'fs2', 1166)" onmouseover="showTip(event, 'fs2', 1166)" class="uc">Ok</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs205', 1167)" onmouseover="showTip(event, 'fs205', 1167)" class="id">a</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs203', 1168)" onmouseover="showTip(event, 'fs203', 1168)" class="fn">cont</span>
<span class="pn">)</span>
<span class="pn">)</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs90', 1169)" onmouseover="showTip(event, 'fs90', 1169)" class="fn">ignore</span>
<span onmouseout="hideTip(event, 'fs81', 1170)" onmouseover="showTip(event, 'fs81', 1170)" class="rt">ThreadPool</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs82', 1171)" onmouseover="showTip(event, 'fs82', 1171)" class="id">QueueUserWorkItem</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">_</span> <span class="k">-></span>
<span onmouseout="hideTip(event, 'fs202', 1172)" onmouseover="showTip(event, 'fs202', 1172)" class="fn">run2</span> <span onmouseout="hideTip(event, 'fs190', 1173)" onmouseover="showTip(event, 'fs190', 1173)" class="id">envChild</span> <span class="pn">(</span><span class="k">fun</span> <span onmouseout="hideTip(event, 'fs206', 1174)" onmouseover="showTip(event, 'fs206', 1174)" class="id">a</span> <span class="k">-></span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs85', 1175)" onmouseover="showTip(event, 'fs85', 1175)" class="rt">Interlocked</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs199', 1176)" onmouseover="showTip(event, 'fs199', 1176)" class="id">Exchange</span><span class="pn">(</span><span class="o">&</span><span onmouseout="hideTip(event, 'fs204', 1177)" onmouseover="showTip(event, 'fs204', 1177)" class="mv">o</span><span class="pn">,</span><span class="n">1</span><span class="pn">)</span> <span class="o">=</span> <span class="n">0</span> <span class="k">then</span>
<span onmouseout="hideTip(event, 'fs38', 1178)" onmouseover="showTip(event, 'fs38', 1178)" class="m">Cancel</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs31', 1179)" onmouseover="showTip(event, 'fs31', 1179)" class="id">set</span> <span onmouseout="hideTip(event, 'fs190', 1180)" onmouseover="showTip(event, 'fs190', 1180)" class="id">envChild</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs38', 1181)" onmouseover="showTip(event, 'fs38', 1181)" class="m">Cancel</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs24', 1182)" onmouseover="showTip(event, 'fs24', 1182)" class="id">isSet</span> <span onmouseout="hideTip(event, 'fs44', 1183)" onmouseover="showTip(event, 'fs44', 1183)" class="id">env</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs203', 1184)" onmouseover="showTip(event, 'fs203', 1184)" class="fn">cont</span> <span onmouseout="hideTip(event, 'fs46', 1185)" onmouseover="showTip(event, 'fs46', 1185)" class="uc">None</span>
<span class="k">else</span> <span onmouseout="hideTip(event, 'fs48', 1186)" onmouseover="showTip(event, 'fs48', 1186)" class="m">Option</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs49', 1187)" onmouseover="showTip(event, 'fs49', 1187)" class="id">map</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs131', 1188)" onmouseover="showTip(event, 'fs131', 1188)" class="m">Result</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs157', 1189)" onmouseover="showTip(event, 'fs157', 1189)" class="id">map</span> <span onmouseout="hideTip(event, 'fs15', 1190)" onmouseover="showTip(event, 'fs15', 1190)" class="uc">Choice1Of2</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs206', 1191)" onmouseover="showTip(event, 'fs206', 1191)" class="id">a</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs203', 1192)" onmouseover="showTip(event, 'fs203', 1192)" class="fn">cont</span>
<span class="pn">)</span>
<span class="pn">)</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs90', 1193)" onmouseover="showTip(event, 'fs90', 1193)" class="fn">ignore</span>
<span class="pn">)</span>
</code></pre>
<p>With <a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion/IO.fs">IO</a> async is implemented directly using the thread pool.
There are two main reasons for this.
In <a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion/IO.fs">IO</a> exceptions are not part of control flow.
Errors are first class and type-safe. Unrecoverable exceptions output the stack trace and exit the process.
Cancellation is fully integrated into <a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion/IO.fs">IO</a> meaning in race,
parallel and upon an error, computations are automatically cancelled, saving resources.</p>
<p>These with the final part dramatically simplify and optimise asynchronous IO code.</p>
<h2><a name="Result" class="anchor" href="#Result">Result</a></h2>
<p>The result part of <a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion/IO.fs">IO</a> represents possible errors in an integrated and type-safe way.
The error type is inferred, and different error types are auto lifted into <code>Choice<'a,'b></code> when combined.
<a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion/IO.fs">IO</a> computations can be timed out and retried based on result using simple functions.
Schedule is a powerful construct that can be combined several ways.
I've replicated the structure from ZIO but not fully explored its uses.</p>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="k">let</span> <span onmouseout="hideTip(event, 'fs249', 1381)" onmouseover="showTip(event, 'fs249', 1381)" class="fn">programRetry</span> <span onmouseout="hideTip(event, 'fs250', 1382)" onmouseover="showTip(event, 'fs250', 1382)" class="id">noRetry</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs224', 1383)" onmouseover="showTip(event, 'fs224', 1383)" class="k">io</span> <span class="pn">{</span>
<span class="k">do!</span> <span onmouseout="hideTip(event, 'fs251', 1384)" onmouseover="showTip(event, 'fs251', 1384)" class="m">Logger</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs238', 1385)" onmouseover="showTip(event, 'fs238', 1385)" class="id">log</span> <span class="s">"started"</span>
<span class="k">do!</span> <span onmouseout="hideTip(event, 'fs252', 1386)" onmouseover="showTip(event, 'fs252', 1386)" class="m">Console</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs229', 1387)" onmouseover="showTip(event, 'fs229', 1387)" class="id">writeLine</span> <span class="s">"Please enter your name:"</span>
<span class="k">let!</span> <span onmouseout="hideTip(event, 'fs253', 1388)" onmouseover="showTip(event, 'fs253', 1388)" class="id">name</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs252', 1389)" onmouseover="showTip(event, 'fs252', 1389)" class="m">Console</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs234', 1390)" onmouseover="showTip(event, 'fs234', 1390)" class="id">readLine</span><span class="pn">(</span><span class="pn">)</span>
<span class="k">do!</span> <span onmouseout="hideTip(event, 'fs251', 1391)" onmouseover="showTip(event, 'fs251', 1391)" class="m">Logger</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs238', 1392)" onmouseover="showTip(event, 'fs238', 1392)" class="id">log</span> <span class="pn">(</span><span class="s">"got name = "</span> <span class="o">+</span> <span onmouseout="hideTip(event, 'fs253', 1393)" onmouseover="showTip(event, 'fs253', 1393)" class="id">name</span><span class="pn">)</span>
<span class="k">let!</span> <span onmouseout="hideTip(event, 'fs254', 1394)" onmouseover="showTip(event, 'fs254', 1394)" class="id">thread</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs255', 1395)" onmouseover="showTip(event, 'fs255', 1395)" class="m">Persistence</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs245', 1396)" onmouseover="showTip(event, 'fs245', 1396)" class="id">persist</span> <span onmouseout="hideTip(event, 'fs253', 1397)" onmouseover="showTip(event, 'fs253', 1397)" class="id">name</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs218', 1398)" onmouseover="showTip(event, 'fs218', 1398)" class="m">IO</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs207', 1399)" onmouseover="showTip(event, 'fs207', 1399)" class="id">timeout</span> <span class="n">1000</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs218', 1400)" onmouseover="showTip(event, 'fs218', 1400)" class="m">IO</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs182', 1401)" onmouseover="showTip(event, 'fs182', 1401)" class="id">retry</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs171', 1402)" onmouseover="showTip(event, 'fs171', 1402)" class="m">Schedule</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs127', 1403)" onmouseover="showTip(event, 'fs127', 1403)" class="id">recurs</span> <span onmouseout="hideTip(event, 'fs250', 1404)" onmouseover="showTip(event, 'fs250', 1404)" class="id">noRetry</span><span class="pn">)</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs218', 1405)" onmouseover="showTip(event, 'fs218', 1405)" class="m">IO</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs184', 1406)" onmouseover="showTip(event, 'fs184', 1406)" class="id">fork</span>
<span class="k">do!</span> <span onmouseout="hideTip(event, 'fs252', 1407)" onmouseover="showTip(event, 'fs252', 1407)" class="m">Console</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs229', 1408)" onmouseover="showTip(event, 'fs229', 1408)" class="id">writeLine</span> <span class="pn">(</span><span class="s">"Hi "</span><span class="o">+</span><span onmouseout="hideTip(event, 'fs253', 1409)" onmouseover="showTip(event, 'fs253', 1409)" class="id">name</span><span class="pn">)</span>
<span class="k">do!</span> <span onmouseout="hideTip(event, 'fs254', 1410)" onmouseover="showTip(event, 'fs254', 1410)" class="id">thread</span>
<span class="k">do!</span> <span onmouseout="hideTip(event, 'fs251', 1411)" onmouseover="showTip(event, 'fs251', 1411)" class="m">Logger</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs238', 1412)" onmouseover="showTip(event, 'fs238', 1412)" class="id">log</span> <span class="s">"finished"</span>
<span class="k">return</span> <span class="n">0</span>
<span class="pn">}</span>
</code></pre>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p>When type inference worked for the dependencies I was surprised.
When it was also possible to make it work for the errors I was amazed.</p>
<p>Computation expressions do not compose well.
At the IO layer a solution is needed for dependencies in a testable way.
The IO layer also needs to efficiently use the thread pool.
Making errors type-safe and integrated in the IO logic completes this compelling trinity.</p>
<h2><a name="References" class="anchor" href="#References">References</a></h2>
<p><a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion/IO.fs">IO.fs</a><br />
<a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion.Tests/IOTests.fs">IOTests.fs</a><br />
<a href="https://scalaz.github.io/scalaz-zio/overview/">ZIO Overview</a><br />
<a href="https://scalaz.github.io/scalaz-zio/datatypes/">ZIO Data Types</a><br />
<a href="https://www.slideshare.net/jdegoes/the-death-of-final-tagless">The Death Of Final Tagless</a></p>
<h2><a name="Thanks" class="anchor" href="#Thanks">Thanks</a></h2>
<p><a href="https://twitter.com/jdegoes">@jdegoes</a> for ZIO and a great talk that made me want to do this.<br />
<a href="https://twitter.com/NinoFloris">@NinoFloris</a> for useful async discussions.<br />
<a href="https://twitter.com/keithtpinson/status/1104071022544932866">@keithtpinson</a> for the error auto lift idea.</p>
<div class="tip" id="fs1">Multiple items<br />module Result<br /><br />from Microsoft.FSharp.Core<br /><br />--------------------<br />[<Struct>]<br />type Result<'T,'TError> =<br />  | Ok of ResultValue: 'T<br />  | Error of ErrorValue: 'TError</div>
<div class="tip" id="fs2">union case Result.Ok: 'a -> Result<'a,'e></div>
<div class="tip" id="fs3">union case Result.Error: 'e -> Result<'a,'e></div>
<div class="tip" id="fs4">Multiple items<br />module Result<br /><br />from Microsoft.FSharp.Core<br /><br />--------------------<br />type Result<'a,'e> =<br />  | Ok of 'a<br />  | Error of 'e</div>
<div class="tip" id="fs5">val map : f:('a -> 'b) -> Result<'a,'e> -> Result<'b,'e></div>
<div class="tip" id="fs6">val f : ('a -> 'b)</div>
<div class="tip" id="fs7">val failwith : message:string -> 'T</div>
<div class="tip" id="fs8">val mapError : f:('e -> 'f) -> Result<'a,'e> -> Result<'a,'f></div>
<div class="tip" id="fs9">val f : ('e -> 'f)</div>
<div class="tip" id="fs10">Multiple items<br />union case Time.Time: Time<br /><br />--------------------<br />type Time = | Time</div>
<div class="tip" id="fs11">val now : unit -> Time</div>
<div class="tip" id="fs12">Multiple items<br />type Choice<'T1,'T2> =<br />  | Choice1Of2 of 'T1<br />  | Choice2Of2 of 'T2<br /><br />--------------------<br />type Choice<'T1,'T2,'T3> =<br />  | Choice1Of3 of 'T1<br />  | Choice2Of3 of 'T2<br />  | Choice3Of3 of 'T3<br /><br />--------------------<br />type Choice<'T1,'T2,'T3,'T4> =<br />  | Choice1Of4 of 'T1<br />  | Choice2Of4 of 'T2<br />  | Choice3Of4 of 'T3<br />  | Choice4Of4 of 'T4<br /><br />--------------------<br />type Choice<'T1,'T2,'T3,'T4,'T5> =<br />  | Choice1Of5 of 'T1<br />  | Choice2Of5 of 'T2<br />  | Choice3Of5 of 'T3<br />  | Choice4Of5 of 'T4<br />  | Choice5Of5 of 'T5<br /><br />--------------------<br />type Choice<'T1,'T2,'T3,'T4,'T5,'T6> =<br />  | Choice1Of6 of 'T1<br />  | Choice2Of6 of 'T2<br />  | Choice3Of6 of 'T3<br />  | Choice4Of6 of 'T4<br />  | Choice5Of6 of 'T5<br />  | Choice6Of6 of 'T6<br /><br />--------------------<br />type Choice<'T1,'T2,'T3,'T4,'T5,'T6,'T7> =<br />  | Choice1Of7 of 'T1<br />  | Choice2Of7 of 'T2<br />  | Choice3Of7 of 'T3<br />  | Choice4Of7 of 'T4<br />  | Choice5Of7 of 'T5<br />  | Choice6Of7 of 'T6<br />  | Choice7Of7 of 'T7</div>
<div class="tip" id="fs13">val merge : c:Choice<'a,'a> -> 'a</div>
<div class="tip" id="fs14">val c : Choice<'a,'a></div>
<div class="tip" id="fs15">union case Choice.Choice1Of2: 'T1 -> Choice<'T1,'T2></div>
<div class="tip" id="fs16">val a : 'a</div>
<div class="tip" id="fs17">union case Choice.Choice2Of2: 'T2 -> Choice<'T1,'T2></div>
<div class="tip" id="fs18">namespace System</div>
<div class="tip" id="fs19">namespace System.Threading</div>
<div class="tip" id="fs20">Multiple items<br />union case Cancel.Cancel: bool ref * children: Cancel list ref -> Cancel<br /><br />--------------------<br />type Cancel = private | Cancel of bool ref * children: Cancel list ref</div>
<div class="tip" id="fs21">type bool = System.Boolean</div>
<div class="tip" id="fs22">Multiple items<br />val ref : value:'T -> 'T ref<br /><br />--------------------<br />type 'T ref = Ref<'T></div>
<div class="tip" id="fs23">type 'T list = List<'T></div>
<div class="tip" id="fs24">val internal isSet : 'r * Cancel -> bool</div>
<div class="tip" id="fs25">val i : bool ref</div>
<div class="tip" id="fs26">val internal create : unit -> Cancel</div>
<div class="tip" id="fs27">val internal add : r:'r * Cancel -> 'r * Cancel</div>
<div class="tip" id="fs28">val r : 'r</div>
<div class="tip" id="fs29">val c : Cancel list ref</div>
<div class="tip" id="fs30">val i : Cancel</div>
<div class="tip" id="fs31">val internal set : r:'a * Cancel -> unit</div>
<div class="tip" id="fs32">val r : 'a</div>
<div class="tip" id="fs33">val me : bool ref</div>
<div class="tip" id="fs34">val kids : Cancel list ref</div>
<div class="tip" id="fs35">Multiple items<br />module List<br /><br />from Microsoft.FSharp.Collections<br /><br />--------------------<br />type List<'T> =<br />  | ( [] )<br />  | ( :: ) of Head: 'T * Tail: 'T list<br />    interface IReadOnlyList<'T><br />    interface IReadOnlyCollection<'T><br />    interface IEnumerable<br />    interface IEnumerable<'T><br />    member GetSlice : startIndex:int option * endIndex:int option -> 'T list<br />    member Head : 'T<br />    member IsEmpty : bool<br />    member Item : index:int -> 'T with get<br />    member Length : int<br />    member Tail : 'T list<br />    ...</div>
<div class="tip" id="fs36">val iter : action:('T -> unit) -> list:'T list -> unit</div>
<div class="tip" id="fs37">Multiple items<br />union case UIO.UIO: ('r * Cancel -> ('a option -> unit) -> unit) -> UIO<'r,'a><br /><br />--------------------<br />type UIO<'r,'a> =<br />  | UIO of ('r * Cancel -> ('a option -> unit) -> unit)<br />    member Bind : f:('a -> UIO<'r,'b>) -> UIO<'r,'b><br />    member Bind : f:('a -> IO<'r,'b,'e>) -> IO<'r,'b,'e></div>
<div class="tip" id="fs38">Multiple items<br />union case Cancel.Cancel: bool ref * children: Cancel list ref -> Cancel<br /><br />--------------------<br />module Cancel<br /><br />from 2019-03-29-io<br /><br />--------------------<br />type Cancel = private | Cancel of bool ref * children: Cancel list ref</div>
<div class="tip" id="fs39">type 'T option = Option<'T></div>
<div class="tip" id="fs40">type unit = Unit</div>
<div class="tip" id="fs41">val m : UIO<'r,'a></div>
<div class="tip" id="fs42">val f : ('a -> UIO<'r,'b>)</div>
<div class="tip" id="fs43">val run : ('r * Cancel -> ('a option -> unit) -> unit)</div>
<div class="tip" id="fs44">val env : 'r * Cancel</div>
<div class="tip" id="fs45">val cont : ('b option -> unit)</div>
<div class="tip" id="fs46">union case Option.None: Option<'T></div>
<div class="tip" id="fs47">val o : 'a option</div>
<div class="tip" id="fs48">module Option<br /><br />from Microsoft.FSharp.Core</div>
<div class="tip" id="fs49">val map : mapping:('T -> 'U) -> option:'T option -> 'U option</div>
<div class="tip" id="fs50">union case Option.Some: Value: 'T -> Option<'T></div>
<div class="tip" id="fs51">val run : ('r * Cancel -> ('b option -> unit) -> unit)</div>
<div class="tip" id="fs52">val result : a:'a -> UIO<'r,'a></div>
<div class="tip" id="fs53">val cont : ('a option -> unit)</div>
<div class="tip" id="fs54">val effect : f:('r -> 'a) -> UIO<'r,'a></div>
<div class="tip" id="fs55">val f : ('r -> 'a)</div>
<div class="tip" id="fs56">val fst : tuple:('T1 * 'T2) -> 'T1</div>
<div class="tip" id="fs57">val map : f:('a -> 'b) -> UIO<'r,'a> -> UIO<'r,'b></div>
<div class="tip" id="fs58">val b : 'b</div>
<div class="tip" id="fs59">val delay : milliseconds:int -> UIO<'r,unit></div>
<div class="tip" id="fs60">val milliseconds : int</div>
<div class="tip" id="fs61">val cont : (unit option -> unit)</div>
<div class="tip" id="fs62">val mutable t : Timer</div>
<div class="tip" id="fs63">module Unchecked<br /><br />from Microsoft.FSharp.Core.Operators</div>
<div class="tip" id="fs64">val defaultof<'T> : 'T</div>
<div class="tip" id="fs65">Multiple items<br />type Timer =<br />  inherit MarshalByRefObject<br />  new : callback:TimerCallback -> Timer + 4 overloads<br />  member Change : dueTime:int * period:int -> bool + 3 overloads<br />  member Dispose : unit -> unit + 1 overload<br /><br />--------------------<br />Timer(callback: TimerCallback) : Timer<br />Timer(callback: TimerCallback, state: obj, dueTime: int, period: int) : Timer<br />Timer(callback: TimerCallback, state: obj, dueTime: System.TimeSpan, period: System.TimeSpan) : Timer<br />Timer(callback: TimerCallback, state: obj, dueTime: uint32, period: uint32) : Timer<br />Timer(callback: TimerCallback, state: obj, dueTime: int64, period: int64) : Timer</div>
<div class="tip" id="fs66">Timer.Dispose() : unit<br />Timer.Dispose(notifyObject: WaitHandle) : bool</div>
<div class="tip" id="fs67">type Timeout =<br />  static val InfiniteTimeSpan : TimeSpan<br />  static val Infinite : int</div>
<div class="tip" id="fs68">field Timeout.Infinite: int = -1</div>
<div class="tip" id="fs69">val flatten : f:('r -> UIO<'r,'a>) -> UIO<'r,'a></div>
<div class="tip" id="fs70">val f : ('r -> UIO<'r,'a>)</div>
<div class="tip" id="fs71">val t : 'a option</div>
<div class="tip" id="fs72">val toAsync : env:'r -> UIO<'r,'a> -> Async<'a></div>
<div class="tip" id="fs73">val env : 'r</div>
<div class="tip" id="fs74">Multiple items<br />type Async =<br />  static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)<br />  static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)<br />  static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool><br />  static member AwaitTask : task:Task -> Async<unit><br />  static member AwaitTask : task:Task<'T> -> Async<'T><br />  static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool><br />  static member CancelDefaultToken : unit -> unit<br />  static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>><br />  static member Choice : computations:seq<Async<'T option>> -> Async<'T option><br />  static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T><br />  ...<br /><br />--------------------<br />type Async<'T> =</div>
<div class="tip" id="fs75">static member Async.FromContinuations : callback:(('T -> unit) * (exn -> unit) * (System.OperationCanceledException -> unit) -> unit) -> Async<'T></div>
<div class="tip" id="fs76">val cont : ('a -> unit)</div>
<div class="tip" id="fs77">property Option.Value: 'a</div>
<div class="tip" id="fs78">val fork : UIO<'r,'a> -> UIO<'r,UIO<'r,'a>></div>
<div class="tip" id="fs79">val contFork : (UIO<'r,'a> option -> unit)</div>
<div class="tip" id="fs80">val mutable o : obj</div>
<div class="tip" id="fs81">type ThreadPool =<br />  static member BindHandle : osHandle:nativeint -> bool + 1 overload<br />  static member GetAvailableThreads : workerThreads:int * completionPortThreads:int -> unit<br />  static member GetMaxThreads : workerThreads:int * completionPortThreads:int -> unit<br />  static member GetMinThreads : workerThreads:int * completionPortThreads:int -> unit<br />  static member QueueUserWorkItem : callBack:WaitCallback -> bool + 1 overload<br />  static member RegisterWaitForSingleObject : waitObject:WaitHandle * callBack:WaitOrTimerCallback * state:obj * millisecondsTimeOutInterval:uint32 * executeOnlyOnce:bool -> RegisteredWaitHandle + 3 overloads<br />  static member SetMaxThreads : workerThreads:int * completionPortThreads:int -> bool<br />  static member SetMinThreads : workerThreads:int * completionPortThreads:int -> bool<br />  static member UnsafeQueueNativeOverlapped : overlapped:NativeOverlapped -> bool<br />  static member UnsafeQueueUserWorkItem : callBack:WaitCallback * state:obj -> bool<br />  ...</div>
<div class="tip" id="fs82">ThreadPool.QueueUserWorkItem(callBack: WaitCallback) : bool<br />ThreadPool.QueueUserWorkItem(callBack: WaitCallback, state: obj) : bool</div>
<div class="tip" id="fs83">val a : 'a option</div>
<div class="tip" id="fs84">val o : obj</div>
<div class="tip" id="fs85">type Interlocked =<br />  static member Add : location1:int * value:int -> int + 1 overload<br />  static member CompareExchange : location1:int * value:int * comparand:int -> int + 6 overloads<br />  static member Decrement : location:int -> int + 1 overload<br />  static member Exchange : location1:int * value:int -> int + 6 overloads<br />  static member Increment : location:int -> int + 1 overload<br />  static member MemoryBarrier : unit -> unit<br />  static member Read : location:int64 -> int64</div>
<div class="tip" id="fs86">Interlocked.CompareExchange<'T (requires reference type)>(location1: byref<'T>, value: 'T, comparand: 'T) : 'T<br />Interlocked.CompareExchange(location1: byref<nativeint>, value: nativeint, comparand: nativeint) : nativeint<br />Interlocked.CompareExchange(location1: byref<obj>, value: obj, comparand: obj) : obj<br />Interlocked.CompareExchange(location1: byref<float>, value: float, comparand: float) : float<br />Interlocked.CompareExchange(location1: byref<float32>, value: float32, comparand: float32) : float32<br />Interlocked.CompareExchange(location1: byref<int64>, value: int64, comparand: int64) : int64<br />Interlocked.CompareExchange(location1: byref<int>, value: int, comparand: int) : int</div>
<div class="tip" id="fs87">val isNull : value:'T -> bool (requires 'T : null)</div>
<div class="tip" id="fs88">val not : value:bool -> bool</div>
<div class="tip" id="fs89">val cont : (Option<'a> -> unit)</div>
<div class="tip" id="fs90">val ignore : value:'T -> unit</div>
<div class="tip" id="fs91">type ClockService =<br />  interface<br />    abstract member Sleep : int -> UIO<'r,unit><br />    abstract member Time : unit -> UIO<'r,Time><br />  end</div>
<div class="tip" id="fs92">Multiple items<br />module Time<br /><br />from 2019-03-29-io<br /><br />--------------------<br />type Time = | Time</div>
<div class="tip" id="fs93">Multiple items<br />union case UIO.UIO: ('r * Cancel -> ('a option -> unit) -> unit) -> UIO<'r,'a><br /><br />--------------------<br />module UIO<br /><br />from 2019-03-29-io<br /><br />--------------------<br />type UIO<'r,'a> =<br />  | UIO of ('r * Cancel -> ('a option -> unit) -> unit)<br />    member Bind : f:('a -> UIO<'r,'b>) -> UIO<'r,'b><br />    member Bind : f:('a -> IO<'r,'b,'e>) -> IO<'r,'b,'e></div>
<div class="tip" id="fs94">Multiple items<br />val int : value:'T -> int (requires member op_Explicit)<br /><br />--------------------<br />type int = int32<br /><br />--------------------<br />type int<'Measure> = int</div>
<div class="tip" id="fs95">type Clock =<br />  interface<br />    abstract member Clock : ClockService<br />  end</div>
<div class="tip" id="fs96">val time : unit -> UIO<#Clock,Time></div>
<div class="tip" id="fs97">val c : #Clock</div>
<div class="tip" id="fs98">property Clock.Clock: ClockService</div>
<div class="tip" id="fs99">abstract member ClockService.Time : unit -> UIO<'r,Time></div>
<div class="tip" id="fs100">val sleep : milliseconds:int -> UIO<#Clock,unit></div>
<div class="tip" id="fs101">abstract member ClockService.Sleep : int -> UIO<'r,unit></div>
<div class="tip" id="fs102">val liveService : ClockService</div>
<div class="tip" id="fs103">Multiple items<br />union case Time.Time: Time<br /><br />--------------------<br />module Time<br /><br />from 2019-03-29-io<br /><br />--------------------<br />type Time = | Time</div>
<div class="tip" id="fs104">val __ : ClockService</div>
<div class="tip" id="fs105">Multiple items<br />union case Decision.Decision: cont: bool * delay: int * state: 'a * (unit -> 'b) -> Decision<'a,'b><br /><br />--------------------<br />type Decision<'a,'b> = | Decision of cont: bool * delay: int * state: 'a * (unit -> 'b)</div>
<div class="tip" id="fs106">Multiple items<br />union case Schedule.Schedule: initial: UIO<'r,'s> * update: 'a * 's -> UIO<'r,Decision<'s,'b>> -> Schedule<'r,'s,'a,'b><br /><br />--------------------<br />type Schedule<'r,'s,'a,'b> = private | Schedule of initial: UIO<'r,'s> * update: 'a * 's -> UIO<'r,Decision<'s,'b>></div>
<div class="tip" id="fs107">val forever<'r,'a> : Schedule<'r,int,'a,int></div>
<div class="tip" id="fs108">val s : int</div>
<div class="tip" id="fs109">val private updated : update:(('a * 's -> UIO<'r,Decision<'s,'b>>) -> 'a * 's -> UIO<'r,Decision<'s,'b2>>) -> Schedule<'r,'s,'a,'b> -> Schedule<'r,'s,'a,'b2></div>
<div class="tip" id="fs110">val update : (('a * 's -> UIO<'r,Decision<'s,'b>>) -> 'a * 's -> UIO<'r,Decision<'s,'b2>>)</div>
<div class="tip" id="fs111">val i : UIO<'r,'s></div>
<div class="tip" id="fs112">val u : ('a * 's -> UIO<'r,Decision<'s,'b>>)</div>
<div class="tip" id="fs113">val private check : test:('a * 'b -> UIO<'r,bool>) -> m:Schedule<'r,'a0,'a,'b> -> Schedule<'r,'a0,'a,'b></div>
<div class="tip" id="fs114">val test : ('a * 'b -> UIO<'r,bool>)</div>
<div class="tip" id="fs115">val m : Schedule<'r,'a,'a0,'b></div>
<div class="tip" id="fs116">val update : ('a * 'a0 -> UIO<'r,Decision<'a0,'b>>)</div>
<div class="tip" id="fs117">val s : 'a</div>
<div class="tip" id="fs118">val cont : bool</div>
<div class="tip" id="fs119">val dur : int</div>
<div class="tip" id="fs120">val a1 : 'a</div>
<div class="tip" id="fs121">val fb : (unit -> 'b)</div>
<div class="tip" id="fs122">val d : Decision<'a,'b></div>
<div class="tip" id="fs123">val b : bool</div>
<div class="tip" id="fs124">val whileOutput : f:('b -> bool) -> m:Schedule<'a,'b0,'c,'b> -> Schedule<'a,'b0,'c,'b></div>
<div class="tip" id="fs125">val f : ('b -> bool)</div>
<div class="tip" id="fs126">val m : Schedule<'a,'b,'c,'b0></div>
<div class="tip" id="fs127">val recurs : n:int -> Schedule<'a,int,'b,int></div>
<div class="tip" id="fs128">val n : int</div>
<div class="tip" id="fs129">val i : int</div>
<div class="tip" id="fs130">Multiple items<br />union case IO.IO: ('r * Cancel -> (Result<'a,'e> option -> unit) -> unit) -> IO<'r,'a,'e><br /><br />--------------------<br />type IO<'r,'a,'e> =<br />  | IO of ('r * Cancel -> (Result<'a,'e> option -> unit) -> unit)<br />    member Bind : f:('a -> UIO<'r,'b>) -> IO<'r,'b,'e><br />    member Bind : f:('a -> IO<'r,'b,'e>) -> IO<'r,'b,'e><br />    member Bind : f:('a -> IO<'r,'b,'e2>) -> IO<'r,'b,Choice<'e,'e2>></div>
<div class="tip" id="fs131">Multiple items<br />module Result<br /><br />from 2019-03-29-io<br /><br />--------------------<br />module Result<br /><br />from Microsoft.FSharp.Core<br /><br />--------------------<br />type Result<'a,'e> =<br />  | Ok of 'a<br />  | Error of 'e</div>
<div class="tip" id="fs132">val m : IO<'r,'a,'e></div>
<div class="tip" id="fs133">val run : ('r * Cancel -> (Result<'a,'e> option -> unit) -> unit)</div>
<div class="tip" id="fs134">val cont : (Result<'b,'e> option -> unit)</div>
<div class="tip" id="fs135">val o : Result<'a,'e> option</div>
<div class="tip" id="fs136">val o : 'b option</div>
<div class="tip" id="fs137">val e : 'e</div>
<div class="tip" id="fs138">val f : ('a -> IO<'r,'b,'e>)</div>
<div class="tip" id="fs139">val run : ('r * Cancel -> (Result<'b,'e> option -> unit) -> unit)</div>
<div class="tip" id="fs140">val f : ('a -> IO<'r,'b,'e2>)</div>
<div class="tip" id="fs141">Multiple items<br />module Choice<br /><br />from 2019-03-29-io<br /><br />--------------------<br />type Choice<'T1,'T2> =<br />  | Choice1Of2 of 'T1<br />  | Choice2Of2 of 'T2<br /><br />--------------------<br />type Choice<'T1,'T2,'T3> =<br />  | Choice1Of3 of 'T1<br />  | Choice2Of3 of 'T2<br />  | Choice3Of3 of 'T3<br /><br />--------------------<br />type Choice<'T1,'T2,'T3,'T4> =<br />  | Choice1Of4 of 'T1<br />  | Choice2Of4 of 'T2<br />  | Choice3Of4 of 'T3<br />  | Choice4Of4 of 'T4<br /><br />--------------------<br />type Choice<'T1,'T2,'T3,'T4,'T5> =<br />  | Choice1Of5 of 'T1<br />  | Choice2Of5 of 'T2<br />  | Choice3Of5 of 'T3<br />  | Choice4Of5 of 'T4<br />  | Choice5Of5 of 'T5<br /><br />--------------------<br />type Choice<'T1,'T2,'T3,'T4,'T5,'T6> =<br />  | Choice1Of6 of 'T1<br />  | Choice2Of6 of 'T2<br />  | Choice3Of6 of 'T3<br />  | Choice4Of6 of 'T4<br />  | Choice5Of6 of 'T5<br />  | Choice6Of6 of 'T6<br /><br />--------------------<br />type Choice<'T1,'T2,'T3,'T4,'T5,'T6,'T7> =<br />  | Choice1Of7 of 'T1<br />  | Choice2Of7 of 'T2<br />  | Choice3Of7 of 'T3<br />  | Choice4Of7 of 'T4<br />  | Choice5Of7 of 'T5<br />  | Choice6Of7 of 'T6<br />  | Choice7Of7 of 'T7</div>
<div class="tip" id="fs142">val cont : (Result<'b,Choice<'e,'e2>> option -> unit)</div>
<div class="tip" id="fs143">val bind : ('r * Cancel -> (Result<'b,'e2> option -> unit) -> unit)</div>
<div class="tip" id="fs144">val o : Result<'b,'e2> option</div>
<div class="tip" id="fs145">val b : Result<'b,Choice<'e,'e2>> option</div>
<div class="tip" id="fs146">Multiple items<br />val mapError : f:('e -> 'f) -> Result<'a,'e> -> Result<'a,'f><br /><br />--------------------<br />val mapError : mapping:('TError -> 'U) -> result:Result<'T,'TError> -> Result<'T,'U></div>
<div class="tip" id="fs147">val bind : ('r * Cancel -> (Result<'b,'e> option -> unit) -> unit)</div>
<div class="tip" id="fs148">val ok : a:'a -> IO<'r,'a,'e></div>
<div class="tip" id="fs149">val cont : (Result<'a,'e> option -> unit)</div>
<div class="tip" id="fs150">val error : e:'e -> IO<'r,'a,'e></div>
<div class="tip" id="fs151">val result : a:Result<'a,'e> -> IO<'r,'a,'e></div>
<div class="tip" id="fs152">val a : Result<'a,'e></div>
<div class="tip" id="fs153">val effect : f:('r -> Result<'a,'e>) -> IO<'r,'a,'e></div>
<div class="tip" id="fs154">val f : ('r -> Result<'a,'e>)</div>
<div class="tip" id="fs155">val map : f:('a -> 'b) -> IO<'r,'a,'e> -> IO<'r,'b,'e></div>
<div class="tip" id="fs156">val b : Result<'b,'e> option</div>
<div class="tip" id="fs157">Multiple items<br />val map : f:('a -> 'b) -> Result<'a,'e> -> Result<'b,'e><br /><br />--------------------<br />val map : mapping:('T -> 'U) -> result:Result<'T,'TError> -> Result<'U,'TError></div>
<div class="tip" id="fs158">val mapError : f:('e -> 'e2) -> IO<'r,'a,'e> -> IO<'r,'a,'e2></div>
<div class="tip" id="fs159">val f : ('e -> 'e2)</div>
<div class="tip" id="fs160">val cont : (Result<'a,'e2> option -> unit)</div>
<div class="tip" id="fs161">val b : Result<'a,'e2> option</div>
<div class="tip" id="fs162">val mapResult : f:(Result<'a,'e> -> Result<'b,'e2>) -> IO<'r,'a,'e> -> IO<'r,'b,'e2></div>
<div class="tip" id="fs163">val f : (Result<'a,'e> -> Result<'b,'e2>)</div>
<div class="tip" id="fs164">val cont : (Result<'b,'e2> option -> unit)</div>
<div class="tip" id="fs165">val b : Result<'b,'e2> option</div>
<div class="tip" id="fs166">val private foldM : succ:('a -> IO<'r,'b,'e2>) -> err:('e -> IO<'r,'b,'e2>) -> IO<'r,'a,'e> -> IO<'r,'b,'e2></div>
<div class="tip" id="fs167">val succ : ('a -> IO<'r,'b,'e2>)</div>
<div class="tip" id="fs168">val err : ('e -> IO<'r,'b,'e2>)</div>
<div class="tip" id="fs169">val run : ('r * Cancel -> (Result<'b,'e2> option -> unit) -> unit)</div>
<div class="tip" id="fs170">val private retryOrElse : Schedule<'r,'s,'e,'a> -> orElse:('e * 's -> IO<'r,'b,'e2>) -> io:IO<'r,'a0,'e> -> IO<'r,Choice<'a0,'b>,'e2> (requires 'r :> Clock)</div>
<div class="tip" id="fs171">Multiple items<br />union case Schedule.Schedule: initial: UIO<'r,'s> * update: 'a * 's -> UIO<'r,Decision<'s,'b>> -> Schedule<'r,'s,'a,'b><br /><br />--------------------<br />module Schedule<br /><br />from 2019-03-29-io<br /><br />--------------------<br />type Schedule<'r,'s,'a,'b> = private | Schedule of initial: UIO<'r,'s> * update: 'a * 's -> UIO<'r,Decision<'s,'b>></div>
<div class="tip" id="fs172">val initial : UIO<#Clock,'s></div>
<div class="tip" id="fs173">val update : ('e * 's -> UIO<#Clock,Decision<'s,'a>>)</div>
<div class="tip" id="fs174">val orElse : ('e * 's -> IO<#Clock,'b,'e2>)</div>
<div class="tip" id="fs175">val io : IO<#Clock,'a,'e></div>
<div class="tip" id="fs176">val loop : ('s -> IO<#Clock,Choice<'a,'b>,'e2>)</div>
<div class="tip" id="fs177">val state : 's</div>
<div class="tip" id="fs178">val u : UIO<#Clock,Decision<'s,'a>></div>
<div class="tip" id="fs179">member UIO.Bind : f:('a -> UIO<'r,'b>) -> UIO<'r,'b><br />member UIO.Bind : f:('a -> IO<'r,'b,'e>) -> IO<'r,'b,'e></div>
<div class="tip" id="fs180">val delay : int</div>
<div class="tip" id="fs181">Multiple items<br />module Clock<br /><br />from 2019-03-29-io<br /><br />--------------------<br />type Clock =<br />  interface<br />    abstract member Clock : ClockService<br />  end</div>
<div class="tip" id="fs182">val retry : policy:Schedule<'r,'s,'e,'sb> -> io:IO<'r,'a,'e> -> IO<'r,'a,'e> (requires 'r :> Clock)</div>
<div class="tip" id="fs183">val policy : Schedule<#Clock,'s,'e,'sb></div>
<div class="tip" id="fs184">val fork : IO<'r,'a,'e> -> UIO<'r,IO<'r,'a,'e>></div>
<div class="tip" id="fs185">val contFork : (IO<'r,'a,'e> option -> unit)</div>
<div class="tip" id="fs186">val a : Result<'a,'e> option</div>
<div class="tip" id="fs187">val para : ios:IO<'r,'a,'e> [] -> IO<'r,'a [],'e></div>
<div class="tip" id="fs188">val ios : IO<'r,'a,'e> []</div>
<div class="tip" id="fs189">val cont : (Result<'a [],'e> option -> unit)</div>
<div class="tip" id="fs190">val envChild : 'r * Cancel</div>
<div class="tip" id="fs191">val results : 'a []</div>
<div class="tip" id="fs192">module Array<br /><br />from Microsoft.FSharp.Collections</div>
<div class="tip" id="fs193">val zeroCreate : count:int -> 'T []</div>
<div class="tip" id="fs194">property System.Array.Length: int</div>
<div class="tip" id="fs195">val mutable count : int</div>
<div class="tip" id="fs196">val iteri : action:(int -> 'T -> unit) -> array:'T [] -> unit</div>
<div class="tip" id="fs197">val io : IO<'r,'a,'e></div>
<div class="tip" id="fs198">Interlocked.Decrement(location: byref<int64>) : int64<br />Interlocked.Decrement(location: byref<int>) : int</div>
<div class="tip" id="fs199">Interlocked.Exchange<'T (requires reference type)>(location1: byref<'T>, value: 'T) : 'T<br />Interlocked.Exchange(location1: byref<nativeint>, value: nativeint) : nativeint<br />Interlocked.Exchange(location1: byref<obj>, value: obj) : obj<br />Interlocked.Exchange(location1: byref<float>, value: float) : float<br />Interlocked.Exchange(location1: byref<float32>, value: float32) : float32<br />Interlocked.Exchange(location1: byref<int64>, value: int64) : int64<br />Interlocked.Exchange(location1: byref<int>, value: int) : int</div>
<div class="tip" id="fs200">val race : UIO<'r,'a2> -> IO<'r,'a1,'e1> -> IO<'r,Choice<'a1,'a2>,'e1></div>
<div class="tip" id="fs201">val run1 : ('r * Cancel -> ('a2 option -> unit) -> unit)</div>
<div class="tip" id="fs202">val run2 : ('r * Cancel -> (Result<'a1,'e1> option -> unit) -> unit)</div>
<div class="tip" id="fs203">val cont : (Result<Choice<'a1,'a2>,'e1> option -> unit)</div>
<div class="tip" id="fs204">val mutable o : int</div>
<div class="tip" id="fs205">val a : 'a2 option</div>
<div class="tip" id="fs206">val a : Result<'a1,'e1> option</div>
<div class="tip" id="fs207">val timeout : milliseconds:int -> io:IO<'r,'a,'e> -> IO<'r,'a,'e option> (requires 'r :> Clock)</div>
<div class="tip" id="fs208">val env : #Clock * Cancel</div>
<div class="tip" id="fs209">val cont : (Result<'a,'e option> option -> unit)</div>
<div class="tip" id="fs210">val run : (#Clock * Cancel -> (Result<Choice<'a,unit>,'e> option -> unit) -> unit)</div>
<div class="tip" id="fs211">val o : Result<Choice<'a,unit>,'e> option</div>
<div class="tip" id="fs212">val toAsync : env:'r -> IO<'r,'a,'e> -> Async<Result<'a,'e>></div>
<div class="tip" id="fs213">val cont : (Result<'a,'e> -> unit)</div>
<div class="tip" id="fs214">property Option.Value: Result<'a,'e></div>
<div class="tip" id="fs215">Multiple items<br />type IOBuilder =<br />  new : unit -> IOBuilder<br />  member Bind : io:UIO<'r,'a> * f:('a -> UIO<'r,'b>) -> UIO<'r,'b><br />  member Bind : io:IO<'r,'a,'e> * f:('a -> UIO<'r,'b>) -> IO<'r,'b,'e><br />  member Bind : io:UIO<'r,'a> * f:('a -> IO<'r,'b,'e>) -> IO<'r,'b,'e><br />  member Bind : io:IO<'r,'a,'e> * f:('a -> IO<'r,'b,'e>) -> IO<'r,'b,'e><br />  member Bind : io:IO<'r,'a,'e1> * f:('a -> IO<'r,'b,'e2>) -> IO<'r,'b,Choice<'e1,'e2>><br />  member Return : value:'b -> UIO<'c,'b><br />  member ReturnFrom : value:'a -> 'a<br /><br />--------------------<br />new : unit -> IOBuilder</div>
<div class="tip" id="fs216">val io : UIO<'r,'a></div>
<div class="tip" id="fs217">val __ : IOBuilder</div>
<div class="tip" id="fs218">Multiple items<br />union case IO.IO: ('r * Cancel -> (Result<'a,'e> option -> unit) -> unit) -> IO<'r,'a,'e><br /><br />--------------------<br />module IO<br /><br />from 2019-03-29-io<br /><br />--------------------<br />type IO<'r,'a,'e> =<br />  | IO of ('r * Cancel -> (Result<'a,'e> option -> unit) -> unit)<br />    member Bind : f:('a -> UIO<'r,'b>) -> IO<'r,'b,'e><br />    member Bind : f:('a -> IO<'r,'b,'e>) -> IO<'r,'b,'e><br />    member Bind : f:('a -> IO<'r,'b,'e2>) -> IO<'r,'b,Choice<'e,'e2>></div>
<div class="tip" id="fs219">member IO.Bind : f:('a -> UIO<'r,'b>) -> IO<'r,'b,'e><br />member IO.Bind : f:('a -> IO<'r,'b,'e>) -> IO<'r,'b,'e><br />member IO.Bind : f:('a -> IO<'r,'b,'e2>) -> IO<'r,'b,Choice<'e,'e2>></div>
<div class="tip" id="fs220">val io : IO<'r,'a,'e1></div>
<div class="tip" id="fs221">val value : 'b</div>
<div class="tip" id="fs222">val value : 'a</div>
<div class="tip" id="fs223">Multiple items<br />type AutoOpenAttribute =<br />  inherit Attribute<br />  new : unit -> AutoOpenAttribute<br />  new : path:string -> AutoOpenAttribute<br />  member Path : string<br /><br />--------------------<br />new : unit -> AutoOpenAttribute<br />new : path:string -> AutoOpenAttribute</div>
<div class="tip" id="fs224">val io : IOBuilder</div>
<div class="tip" id="fs225">Multiple items<br />union case ConsoleError.ConsoleError: ConsoleError<br /><br />--------------------<br />type ConsoleError = | ConsoleError</div>
<div class="tip" id="fs226">type ConsoleService =<br />  interface<br />    abstract member ReadLine : unit -> Result<string,ConsoleError><br />    abstract member WriteLine : string -> unit<br />  end</div>
<div class="tip" id="fs227">Multiple items<br />val string : value:'T -> string<br /><br />--------------------<br />type string = System.String</div>
<div class="tip" id="fs228">type Console =<br />  interface<br />    abstract member Console : ConsoleService<br />  end</div>
<div class="tip" id="fs229">val writeLine : s:string -> UIO<#Console,unit></div>
<div class="tip" id="fs230">val s : string</div>
<div class="tip" id="fs231">val c : #Console</div>
<div class="tip" id="fs232">property Console.Console: ConsoleService</div>
<div class="tip" id="fs233">abstract member ConsoleService.WriteLine : string -> unit</div>
<div class="tip" id="fs234">val readLine : unit -> IO<#Console,string,ConsoleError></div>
<div class="tip" id="fs235">abstract member ConsoleService.ReadLine : unit -> Result<string,ConsoleError></div>
<div class="tip" id="fs236">type LoggingService =<br />  interface<br />    abstract member Log : string -> unit<br />  end</div>
<div class="tip" id="fs237">type Logger =<br />  interface<br />    abstract member Logging : LoggingService<br />  end</div>
<div class="tip" id="fs238">val log : s:string -> UIO<#Logger,unit></div>
<div class="tip" id="fs239">val l : #Logger</div>
<div class="tip" id="fs240">property Logger.Logging: LoggingService</div>
<div class="tip" id="fs241">abstract member LoggingService.Log : string -> unit</div>
<div class="tip" id="fs242">Multiple items<br />union case PersistError.PersistError: PersistError<br /><br />--------------------<br />type PersistError = | PersistError</div>
<div class="tip" id="fs243">type PersistenceService =<br />  interface<br />    abstract member Persist : 'a -> Result<unit,PersistError><br />  end</div>
<div class="tip" id="fs244">type Persistence =<br />  interface<br />    abstract member Persistence : PersistenceService<br />  end</div>
<div class="tip" id="fs245">val persist : a:'a -> IO<#Persistence,unit,PersistError></div>
<div class="tip" id="fs246">val p : #Persistence</div>
<div class="tip" id="fs247">property Persistence.Persistence: PersistenceService</div>
<div class="tip" id="fs248">abstract member PersistenceService.Persist : 'a -> Result<unit,PersistError></div>
<div class="tip" id="fs249">val programRetry : noRetry:int -> IO<'r,int,Choice<ConsoleError,PersistError option>> (requires 'r :> Logger and 'r :> Clock and 'r :> Persistence and 'r :> Console)</div>
<div class="tip" id="fs250">val noRetry : int</div>
<div class="tip" id="fs251">Multiple items<br />module Logger<br /><br />from 2019-03-29-io<br /><br />--------------------<br />type Logger =<br />  interface<br />    abstract member Logging : LoggingService<br />  end</div>
<div class="tip" id="fs252">Multiple items<br />module Console<br /><br />from 2019-03-29-io<br /><br />--------------------<br />type Console =<br />  interface<br />    abstract member Console : ConsoleService<br />  end</div>
<div class="tip" id="fs253">val name : string</div>
<div class="tip" id="fs254">val thread : IO<'r,unit,PersistError option> (requires 'r :> Logger and 'r :> Clock and 'r :> Persistence and 'r :> Console)</div>
<div class="tip" id="fs255">Multiple items<br />module Persistence<br /><br />from 2019-03-29-io<br /><br />--------------------<br />type Persistence =<br />  interface<br />    abstract member Persistence : PersistenceService<br />  end</div>
MapSlim - From DictionarySlim to Fsion
2018-12-14T00:00:00+00:00
http://anthonylloyd.github.io/blog/2018/12/14/mapslim
<p>PLEASE NOTE SOURCE CODE IS NO LONGER AVAILABLE AND THE POST IS HERE JUST FOR INFORMATION</p>
<p>This post is part of the <a href="https://sergeytihon.com/2018/10/22/f-advent-calendar-in-english-2018/">F# Advent Calendar 2018</a> series.
Many thanks again to Sergey Tihon for organizing these.</p>
<p>Recently while working on <a href="https://github.com/dotnet/corefxlab/blob/master/src/Microsoft.Experimental.Collections/Microsoft/Collections/Extensions/DictionarySlim.cs">DictionarySlim</a>
and <a href="https://github.com/AnthonyLloyd/Fsion">Fsion</a> it became clear it is possible to create useful lock-free for read collections.</p>
<p><a href="https://github.com/dotnet/corefxlab/blob/master/src/Microsoft.Experimental.Collections/Microsoft/Collections/Extensions/DictionarySlim.cs">DictionarySlim</a>
is a <code>Dictionary</code> replacement that can be up to 2.5 times faster for the common <code>TryGetValue</code> & <code>Add</code> pattern.
To do this it returns <code>ref</code> values.
It is also more efficient with hash codes using <code>&</code> instead of the more costly <code>%</code>.
It came from ideas used in the <a href="https://benchmarksgame-team.pages.debian.net/benchmarksgame/program/knucleotide-csharpcore-9.html">Benchmarks Game</a>.</p>
<p><a href="https://github.com/AnthonyLloyd/Fsion">Fsion</a> is a <a href="https://dictionary.cambridge.org/dictionary/english/wip">[WIP]</a> bi-temporal database for F#.
It stores a compressed set of historic values (with audit) for each entity-attribute.
The idea is that functional techniques can be used to translate unstructured data to fully type safe representations.
Compression and using attribute functions like Excel aims to keep database sizes minimal and possibly in memory.</p>
<h2><a name="Fsion-locking-model" class="anchor" href="#Fsion-locking-model">Fsion locking model</a></h2>
<p>For the in memory version the value <code>DataSeries</code> are stored in a <code>Dictionary<EntityAttribute,DataSeries></code> collection.
Each immutable <code>DataSeries</code> can be atomically replaced.
All queries are executed up to a given transaction id or time.
This simplifies the locking model as entries can be updated at the same time as a consistent set of reads are made.</p>
<p>The problem is that a <code>ReaderWriterLock</code> would still need to be used on the <code>Dictionary</code> for the read and write side.
By creating lock-free for read collections the database can be simplified to fully lock-free for read access.</p>
<h2><a name="MapSlim-SetSlim-and-ListSlim" class="anchor" href="#MapSlim-SetSlim-and-ListSlim">MapSlim, SetSlim and ListSlim</a></h2>
<p>Three new collections <a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion/MapSlim.fs">MapSlim</a>,
<a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion/SetSlim.fs">SetSlim</a>
and <a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion/ListSlim.fs">ListSlim</a> have been created in F#.</p>
<p>The collections are lock-free for read for immutable reference types or types that can be updated atomically.
They are based on and show similar performance to <code>DictionarySlim</code>.
Internally care must be taken to add to and resize the collection atomically.
<code>List</code> would only require a small change for this to be true.</p>
<p>The limitation is the API for these collections is slimmed down and has no <code>Remove</code> or <code>Clear</code>.
One benefit that comes from this is that the collection entries can also be indexed into.</p>
<p>Calling the possibly updating <code>GetRef</code> method must be done using a lock while manipulating the <code>ref</code> value.</p>
<p>These collections have great potential for caches and functionality such as memoize.
The threading model becomes much simpler and eliminates the need to switch to concurrent collections.</p>
<h2><a name="Testing" class="anchor" href="#Testing">Testing</a></h2>
<p>Performance and multithreading testing provides a good demonstration of <a href="https://github.com/haf/expecto">Expecto's</a> abilities.</p>
<p>In under 250 lines of code the <a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion.Tests/MapSlimTests.fs">MapSlimTests</a>
have unit tests, property tests, performance tests and threading stress tests.</p>
<p><img src="/public/mapslim/mapslim_stress.png" alt="mapslim_stress" /></p>
<p>The other collections tests are <a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion.Tests/SetSlimTests.fs">SetSlimTests</a> and
<a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion.Tests/ListSlimTests.fs">ListSlimTests</a></p>
<p><img src="/public/mapslim/mapslim_perf.png" alt="mapslim_perf" /></p>
<p>The tests show large performance improvements over equivalent mutable collections.
In multithreading situations such as server caches this difference will be even greater.</p>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p><a href="https://github.com/AnthonyLloyd/Fsion">Fsion</a> makes use of immutable <code>DataSeries</code> and lock-free read collections like
<a href="https://github.com/AnthonyLloyd/Fsion/blob/master/Fsion/MapSlim.fs">MapSlim</a>
to create a lock-free row versioning style concurrency model.</p>
<p>This produces an interesting hybrid approach possible in a functional-first language.
It simplifies often complex and error prone locking required in server caches.</p>
<p>In future posts I hope to demonstrate how combining all of this can lead to a
simple yet high performance database and server.</p>
<p>Happy holidays!</p>
<h2><a name="References" class="anchor" href="#References">References</a></h2>
<p><a href="https://www.datomic.com/">Datomic</a><br />
<a href="https://vvvvalvalval.github.io/posts/2018-11-12-datomic-event-sourcing-without-the-hassle.html">Datomic: Event Sourcing without the hassle</a><br />
<a href="http://anthonylloyd.github.io/blog/2018/02/01/architecture-data-first">Data-First Architecture</a><br />
<a href="https://github.com/Microsoft/FASTER">FASTER</a></p>
DAG - An Immutable Spreadsheet Data Structure
2018-08-24T00:00:00+01:00
http://anthonylloyd.github.io/blog/2018/08/24/dag-immutable-spreadsheet
<p>In finance, data grids can be defined as a set of input fields and function fields that take other field values as parameters.
Spreadsheets are often used to do this, but they have several <a href="https://www.cio.com/article/2438188/enterprise-software/eight-of-the-worst-spreadsheet-blunders.html">limitations</a>.</p>
<p>Recently, I've been working on ways of describing calculations, so they can just as easily be viewed in a desktop application, web report or spreadsheet.</p>
<p>One of the components required to do this is a functional calculation graph much like how a spreadsheet works.
This blog aims to construct a functional <a href="https://en.wikipedia.org/wiki/Directed_acyclic_graph">directed acyclic graph (DAG)</a> calculation data structure.</p>
<h2><a name="DAG-code" class="anchor" href="#DAG-code">DAG code</a></h2>
<p>We don't have to work very hard to ensure the graph is not circular or keep the cells in topological order.
The API can be designed such that it is only possible to add function cells when the parameter cells already exist in the DAG.
All tasks can be performed with a single pass of the cells in the order they were added.</p>
<p>The DAG data structure is made immutable by cloning any internal arrays when they need to be changed.
Grids can keep the old version of the calculations or compare and switch to the new version when needed.</p>
<p>The DAG is fully type safe by use of an applicative functor builder in constructing function cells.</p>
<p>Cells are evaluated as <code>lazy</code> <code>Task</code> so calculations run as parallel as possible.
Calculations are run only once, and results are reused even after further updates to the DAG.</p>
<p>The code can be downloaded <a href="https://github.com/AnthonyLloyd/Dag">here</a>.</p>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="k">type</span> <span onmouseout="hideTip(event, 'fs4', 5)" onmouseover="showTip(event, 'fs4', 5)" class="rt">Dag</span> <span class="o">=</span> <span class="k">private</span> <span class="pn">{</span>
<span onmouseout="hideTip(event, 'fs5', 6)" onmouseover="showTip(event, 'fs5', 6)" class="id">InputValues</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs6', 7)" onmouseover="showTip(event, 'fs6', 7)" class="rt">obj</span> <span onmouseout="hideTip(event, 'fs7', 8)" onmouseover="showTip(event, 'fs7', 8)" class="rt">array</span>
<span onmouseout="hideTip(event, 'fs8', 9)" onmouseover="showTip(event, 'fs8', 9)" class="id">FunctionInputs</span> <span class="pn">:</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs9', 10)" onmouseover="showTip(event, 'fs9', 10)" class="rt">Set</span><span class="pn"><</span><span onmouseout="hideTip(event, 'fs10', 11)" onmouseover="showTip(event, 'fs10', 11)" class="vt">int</span><span class="pn">></span> <span class="pn">*</span> <span onmouseout="hideTip(event, 'fs9', 12)" onmouseover="showTip(event, 'fs9', 12)" class="rt">Set</span><span class="pn"><</span><span onmouseout="hideTip(event, 'fs10', 13)" onmouseover="showTip(event, 'fs10', 13)" class="vt">int</span><span class="pn">></span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs7', 14)" onmouseover="showTip(event, 'fs7', 14)" class="rt">array</span>
<span onmouseout="hideTip(event, 'fs11', 15)" onmouseover="showTip(event, 'fs11', 15)" class="id">FunctionFunctions</span> <span class="pn">:</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs4', 16)" onmouseover="showTip(event, 'fs4', 16)" class="rt">Dag</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs6', 17)" onmouseover="showTip(event, 'fs6', 17)" class="rt">obj</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs7', 18)" onmouseover="showTip(event, 'fs7', 18)" class="rt">array</span> <span class="c">//obj is Task<'a></span>
<span onmouseout="hideTip(event, 'fs12', 19)" onmouseover="showTip(event, 'fs12', 19)" class="id">FunctionValues</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs13', 20)" onmouseover="showTip(event, 'fs13', 20)" class="rt">Lazy</span><span class="pn"><</span><span onmouseout="hideTip(event, 'fs6', 21)" onmouseover="showTip(event, 'fs6', 21)" class="rt">obj</span><span class="pn">></span> <span onmouseout="hideTip(event, 'fs7', 22)" onmouseover="showTip(event, 'fs7', 22)" class="rt">array</span> <span class="c">//obj is Task<'a></span>
<span class="pn">}</span>
<span class="k">module</span> <span onmouseout="hideTip(event, 'fs4', 23)" onmouseover="showTip(event, 'fs4', 23)" class="m">Dag</span> <span class="o">=</span>
<span class="k">let</span> <span class="k">private</span> <span onmouseout="hideTip(event, 'fs14', 24)" onmouseover="showTip(event, 'fs14', 24)" class="fn">append</span> <span onmouseout="hideTip(event, 'fs15', 25)" onmouseover="showTip(event, 'fs15', 25)" class="id">a</span> <span onmouseout="hideTip(event, 'fs16', 26)" onmouseover="showTip(event, 'fs16', 26)" class="id">v</span> <span class="o">=</span>
<span class="k">let</span> <span class="k">mutable</span> <span onmouseout="hideTip(event, 'fs17', 27)" onmouseover="showTip(event, 'fs17', 27)" class="mv">a</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs15', 28)" onmouseover="showTip(event, 'fs15', 28)" class="id">a</span>
<span onmouseout="hideTip(event, 'fs18', 29)" onmouseover="showTip(event, 'fs18', 29)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs19', 30)" onmouseover="showTip(event, 'fs19', 30)" class="id">Resize</span><span class="pn">(</span><span class="o">&</span><span onmouseout="hideTip(event, 'fs17', 31)" onmouseover="showTip(event, 'fs17', 31)" class="mv">a</span><span class="pn">,</span> <span onmouseout="hideTip(event, 'fs18', 32)" onmouseover="showTip(event, 'fs18', 32)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs20', 33)" onmouseover="showTip(event, 'fs20', 33)" class="id">length</span> <span onmouseout="hideTip(event, 'fs17', 34)" onmouseover="showTip(event, 'fs17', 34)" class="mv">a</span> <span class="o">+</span> <span class="n">1</span><span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs17', 35)" onmouseover="showTip(event, 'fs17', 35)" class="mv">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs18', 36)" onmouseover="showTip(event, 'fs18', 36)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs20', 37)" onmouseover="showTip(event, 'fs20', 37)" class="id">length</span> <span onmouseout="hideTip(event, 'fs17', 38)" onmouseover="showTip(event, 'fs17', 38)" class="mv">a</span> <span class="o">-</span> <span class="n">1</span><span class="pn">]</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs16', 39)" onmouseover="showTip(event, 'fs16', 39)" class="id">v</span>
<span onmouseout="hideTip(event, 'fs17', 40)" onmouseover="showTip(event, 'fs17', 40)" class="mv">a</span>
<span class="k">type</span> <span onmouseout="hideTip(event, 'fs21', 41)" onmouseover="showTip(event, 'fs21', 41)" class="rt">Input</span> <span class="o">=</span> <span class="k">private</span> <span class="pn">|</span> <span onmouseout="hideTip(event, 'fs22', 42)" onmouseover="showTip(event, 'fs22', 42)" class="uc">CellInput</span>
<span class="k">type</span> <span onmouseout="hideTip(event, 'fs23', 43)" onmouseover="showTip(event, 'fs23', 43)" class="rt">Function</span> <span class="o">=</span> <span class="k">private</span> <span class="pn">|</span> <span onmouseout="hideTip(event, 'fs24', 44)" onmouseover="showTip(event, 'fs24', 44)" class="uc">CellFunction</span>
<span class="k">type</span> <span onmouseout="hideTip(event, 'fs25', 45)" onmouseover="showTip(event, 'fs25', 45)" class="rt">Cell</span><span class="pn"><</span><span class="ta">'</span><span class="id">a</span><span class="pn">,</span><span class="ta">'</span><span class="id">b</span><span class="pn">></span> <span class="o">=</span> <span class="k">private</span> <span class="pn">|</span> <span onmouseout="hideTip(event, 'fs25', 46)" onmouseover="showTip(event, 'fs25', 46)" class="uc">Cell</span> <span class="k">of</span> <span onmouseout="hideTip(event, 'fs10', 47)" onmouseover="showTip(event, 'fs10', 47)" class="vt">int</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs26', 48)" onmouseover="showTip(event, 'fs26', 48)" class="id">empty</span> <span class="o">=</span> <span class="pn">{</span>
<span class="id">InputValues</span> <span class="o">=</span> <span class="pn">[|</span><span class="pn">|]</span>
<span class="id">FunctionInputs</span> <span class="o">=</span> <span class="pn">[|</span><span class="pn">|]</span>
<span class="id">FunctionFunctions</span> <span class="o">=</span> <span class="pn">[|</span><span class="pn">|]</span>
<span class="id">FunctionValues</span> <span class="o">=</span> <span class="pn">[|</span><span class="pn">|]</span>
<span class="pn">}</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs27', 49)" onmouseover="showTip(event, 'fs27', 49)" class="fn">addInput</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs16', 50)" onmouseover="showTip(event, 'fs16', 50)" class="id">v</span><span class="pn">:</span><span class="ta">'</span><span class="id">a</span><span class="pn">)</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs28', 51)" onmouseover="showTip(event, 'fs28', 51)" class="id">d</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs4', 52)" onmouseover="showTip(event, 'fs4', 52)" class="rt">Dag</span><span class="pn">)</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs4', 53)" onmouseover="showTip(event, 'fs4', 53)" class="rt">Dag</span> <span class="pn">*</span> <span onmouseout="hideTip(event, 'fs25', 54)" onmouseover="showTip(event, 'fs25', 54)" class="rt">Cell</span><span class="pn"><</span><span class="ta">'</span><span class="id">a</span><span class="pn">,</span> <span onmouseout="hideTip(event, 'fs21', 55)" onmouseover="showTip(event, 'fs21', 55)" class="rt">Input</span><span class="pn">></span> <span class="o">=</span>
<span class="pn">{</span> <span onmouseout="hideTip(event, 'fs28', 56)" onmouseover="showTip(event, 'fs28', 56)" class="id">d</span> <span class="k">with</span>
<span class="id">InputValues</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs29', 57)" onmouseover="showTip(event, 'fs29', 57)" class="fn">box</span> <span onmouseout="hideTip(event, 'fs16', 58)" onmouseover="showTip(event, 'fs16', 58)" class="id">v</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs14', 59)" onmouseover="showTip(event, 'fs14', 59)" class="fn">append</span> <span onmouseout="hideTip(event, 'fs28', 60)" onmouseover="showTip(event, 'fs28', 60)" class="id">d</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs5', 61)" onmouseover="showTip(event, 'fs5', 61)" class="id">InputValues</span>
<span class="pn">}</span><span class="pn">,</span> <span onmouseout="hideTip(event, 'fs25', 62)" onmouseover="showTip(event, 'fs25', 62)" class="uc">Cell</span> <span onmouseout="hideTip(event, 'fs28', 63)" onmouseover="showTip(event, 'fs28', 63)" class="id">d</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs5', 64)" onmouseover="showTip(event, 'fs5', 64)" class="id">InputValues</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs30', 65)" onmouseover="showTip(event, 'fs30', 65)" class="id">Length</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs31', 66)" onmouseover="showTip(event, 'fs31', 66)" class="fn">getValue</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs25', 67)" onmouseover="showTip(event, 'fs25', 67)" class="uc">Cell</span> <span onmouseout="hideTip(event, 'fs32', 68)" onmouseover="showTip(event, 'fs32', 68)" class="id">i</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs25', 69)" onmouseover="showTip(event, 'fs25', 69)" class="rt">Cell</span><span class="pn"><</span><span class="ta">'</span><span class="id">a</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs21', 70)" onmouseover="showTip(event, 'fs21', 70)" class="rt">Input</span><span class="pn">></span><span class="pn">)</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs28', 71)" onmouseover="showTip(event, 'fs28', 71)" class="id">d</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs4', 72)" onmouseover="showTip(event, 'fs4', 72)" class="rt">Dag</span><span class="pn">)</span> <span class="pn">:</span> <span class="ta">'</span><span class="id">a</span> <span class="o">=</span>
<span class="k">downcast</span> <span onmouseout="hideTip(event, 'fs28', 73)" onmouseover="showTip(event, 'fs28', 73)" class="id">d</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs5', 74)" onmouseover="showTip(event, 'fs5', 74)" class="id">InputValues</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs32', 75)" onmouseover="showTip(event, 'fs32', 75)" class="id">i</span><span class="pn">]</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs33', 76)" onmouseover="showTip(event, 'fs33', 76)" class="fn">setInput</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs25', 77)" onmouseover="showTip(event, 'fs25', 77)" class="uc">Cell</span> <span onmouseout="hideTip(event, 'fs32', 78)" onmouseover="showTip(event, 'fs32', 78)" class="id">i</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs25', 79)" onmouseover="showTip(event, 'fs25', 79)" class="rt">Cell</span><span class="pn"><</span><span class="ta">'</span><span class="id">a</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs21', 80)" onmouseover="showTip(event, 'fs21', 80)" class="rt">Input</span><span class="pn">></span><span class="pn">)</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs34', 81)" onmouseover="showTip(event, 'fs34', 81)" class="id">a</span><span class="pn">:</span><span class="ta">'</span><span class="id">a</span><span class="pn">)</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs28', 82)" onmouseover="showTip(event, 'fs28', 82)" class="id">d</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs4', 83)" onmouseover="showTip(event, 'fs4', 83)" class="rt">Dag</span><span class="pn">)</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs4', 84)" onmouseover="showTip(event, 'fs4', 84)" class="rt">Dag</span> <span class="o">=</span>
<span class="k">if</span> <span class="k">downcast</span> <span onmouseout="hideTip(event, 'fs28', 85)" onmouseover="showTip(event, 'fs28', 85)" class="id">d</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs5', 86)" onmouseover="showTip(event, 'fs5', 86)" class="id">InputValues</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs32', 87)" onmouseover="showTip(event, 'fs32', 87)" class="id">i</span><span class="pn">]</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs34', 88)" onmouseover="showTip(event, 'fs34', 88)" class="id">a</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs28', 89)" onmouseover="showTip(event, 'fs28', 89)" class="id">d</span>
<span class="k">else</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs35', 90)" onmouseover="showTip(event, 'fs35', 90)" class="id">dirtyCalcs</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs36', 91)" onmouseover="showTip(event, 'fs36', 91)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs37', 92)" onmouseover="showTip(event, 'fs37', 92)" class="id">fold</span> <span class="pn">(</span><span class="k">fun</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs38', 93)" onmouseover="showTip(event, 'fs38', 93)" class="id">j</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs39', 94)" onmouseover="showTip(event, 'fs39', 94)" class="id">s</span><span class="pn">)</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs40', 95)" onmouseover="showTip(event, 'fs40', 95)" class="id">inputs</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs41', 96)" onmouseover="showTip(event, 'fs41', 96)" class="id">calcInputs</span><span class="pn">)</span> <span class="k">-></span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs9', 97)" onmouseover="showTip(event, 'fs9', 97)" class="m">Set</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs42', 98)" onmouseover="showTip(event, 'fs42', 98)" class="id">contains</span> <span onmouseout="hideTip(event, 'fs32', 99)" onmouseover="showTip(event, 'fs32', 99)" class="id">i</span> <span onmouseout="hideTip(event, 'fs40', 100)" onmouseover="showTip(event, 'fs40', 100)" class="id">inputs</span> <span class="o">||</span>
<span onmouseout="hideTip(event, 'fs9', 101)" onmouseover="showTip(event, 'fs9', 101)" class="m">Set</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs43', 102)" onmouseover="showTip(event, 'fs43', 102)" class="id">intersect</span> <span onmouseout="hideTip(event, 'fs39', 103)" onmouseover="showTip(event, 'fs39', 103)" class="id">s</span> <span onmouseout="hideTip(event, 'fs41', 104)" onmouseover="showTip(event, 'fs41', 104)" class="id">calcInputs</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs9', 105)" onmouseover="showTip(event, 'fs9', 105)" class="m">Set</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs44', 106)" onmouseover="showTip(event, 'fs44', 106)" class="id">isEmpty</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs45', 107)" onmouseover="showTip(event, 'fs45', 107)" class="fn">not</span> <span class="k">then</span>
<span onmouseout="hideTip(event, 'fs38', 108)" onmouseover="showTip(event, 'fs38', 108)" class="id">j</span><span class="o">+</span><span class="n">1</span><span class="pn">,</span> <span onmouseout="hideTip(event, 'fs9', 109)" onmouseover="showTip(event, 'fs9', 109)" class="m">Set</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs46', 110)" onmouseover="showTip(event, 'fs46', 110)" class="id">add</span> <span onmouseout="hideTip(event, 'fs38', 111)" onmouseover="showTip(event, 'fs38', 111)" class="id">j</span> <span onmouseout="hideTip(event, 'fs39', 112)" onmouseover="showTip(event, 'fs39', 112)" class="id">s</span>
<span class="k">else</span>
<span onmouseout="hideTip(event, 'fs38', 113)" onmouseover="showTip(event, 'fs38', 113)" class="id">j</span><span class="o">+</span><span class="n">1</span><span class="pn">,</span> <span onmouseout="hideTip(event, 'fs39', 114)" onmouseover="showTip(event, 'fs39', 114)" class="id">s</span>
<span class="pn">)</span> <span class="pn">(</span><span class="n">0</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs9', 115)" onmouseover="showTip(event, 'fs9', 115)" class="m">Set</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs47', 116)" onmouseover="showTip(event, 'fs47', 116)" class="id">empty</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs28', 117)" onmouseover="showTip(event, 'fs28', 117)" class="id">d</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs8', 118)" onmouseover="showTip(event, 'fs8', 118)" class="id">FunctionInputs</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs48', 119)" onmouseover="showTip(event, 'fs48', 119)" class="fn">snd</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs49', 120)" onmouseover="showTip(event, 'fs49', 120)" class="id">inputValues</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs18', 121)" onmouseover="showTip(event, 'fs18', 121)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs50', 122)" onmouseover="showTip(event, 'fs50', 122)" class="id">copy</span> <span onmouseout="hideTip(event, 'fs28', 123)" onmouseover="showTip(event, 'fs28', 123)" class="id">d</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs5', 124)" onmouseover="showTip(event, 'fs5', 124)" class="id">InputValues</span>
<span onmouseout="hideTip(event, 'fs49', 125)" onmouseover="showTip(event, 'fs49', 125)" class="id">inputValues</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs32', 126)" onmouseover="showTip(event, 'fs32', 126)" class="id">i</span><span class="pn">]</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs29', 127)" onmouseover="showTip(event, 'fs29', 127)" class="fn">box</span> <span onmouseout="hideTip(event, 'fs34', 128)" onmouseover="showTip(event, 'fs34', 128)" class="id">a</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs9', 129)" onmouseover="showTip(event, 'fs9', 129)" class="m">Set</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs44', 130)" onmouseover="showTip(event, 'fs44', 130)" class="id">isEmpty</span> <span onmouseout="hideTip(event, 'fs35', 131)" onmouseover="showTip(event, 'fs35', 131)" class="id">dirtyCalcs</span> <span class="k">then</span> <span class="pn">{</span> <span onmouseout="hideTip(event, 'fs28', 132)" onmouseover="showTip(event, 'fs28', 132)" class="id">d</span> <span class="k">with</span> <span class="id">InputValues</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs49', 133)" onmouseover="showTip(event, 'fs49', 133)" class="id">inputValues</span> <span class="pn">}</span>
<span class="k">else</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs51', 134)" onmouseover="showTip(event, 'fs51', 134)" class="id">functionValues</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs18', 135)" onmouseover="showTip(event, 'fs18', 135)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs50', 136)" onmouseover="showTip(event, 'fs50', 136)" class="id">copy</span> <span onmouseout="hideTip(event, 'fs28', 137)" onmouseover="showTip(event, 'fs28', 137)" class="id">d</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs12', 138)" onmouseover="showTip(event, 'fs12', 138)" class="id">FunctionValues</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs52', 139)" onmouseover="showTip(event, 'fs52', 139)" class="id">dag</span> <span class="o">=</span> <span class="pn">{</span>
<span onmouseout="hideTip(event, 'fs28', 140)" onmouseover="showTip(event, 'fs28', 140)" class="id">d</span> <span class="k">with</span>
<span class="id">InputValues</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs49', 141)" onmouseover="showTip(event, 'fs49', 141)" class="id">inputValues</span>
<span class="id">FunctionValues</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs51', 142)" onmouseover="showTip(event, 'fs51', 142)" class="id">functionValues</span>
<span class="pn">}</span>
<span onmouseout="hideTip(event, 'fs9', 143)" onmouseover="showTip(event, 'fs9', 143)" class="m">Set</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs53', 144)" onmouseover="showTip(event, 'fs53', 144)" class="id">iter</span> <span class="pn">(</span><span class="k">fun</span> <span onmouseout="hideTip(event, 'fs32', 145)" onmouseover="showTip(event, 'fs32', 145)" class="id">i</span> <span class="k">-></span>
<span onmouseout="hideTip(event, 'fs51', 146)" onmouseover="showTip(event, 'fs51', 146)" class="id">functionValues</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs32', 147)" onmouseover="showTip(event, 'fs32', 147)" class="id">i</span><span class="pn">]</span> <span class="k"><-</span> <span class="k">lazy</span> <span onmouseout="hideTip(event, 'fs28', 148)" onmouseover="showTip(event, 'fs28', 148)" class="id">d</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs11', 149)" onmouseover="showTip(event, 'fs11', 149)" class="id">FunctionFunctions</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs32', 150)" onmouseover="showTip(event, 'fs32', 150)" class="id">i</span><span class="pn">]</span> <span onmouseout="hideTip(event, 'fs52', 151)" onmouseover="showTip(event, 'fs52', 151)" class="id">dag</span>
<span class="pn">)</span> <span onmouseout="hideTip(event, 'fs35', 152)" onmouseover="showTip(event, 'fs35', 152)" class="id">dirtyCalcs</span>
<span onmouseout="hideTip(event, 'fs52', 153)" onmouseover="showTip(event, 'fs52', 153)" class="id">dag</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs54', 154)" onmouseover="showTip(event, 'fs54', 154)" class="fn">getValueTask</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs25', 155)" onmouseover="showTip(event, 'fs25', 155)" class="uc">Cell</span> <span onmouseout="hideTip(event, 'fs32', 156)" onmouseover="showTip(event, 'fs32', 156)" class="id">i</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs25', 157)" onmouseover="showTip(event, 'fs25', 157)" class="rt">Cell</span><span class="pn"><</span><span class="ta">'</span><span class="id">a</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs23', 158)" onmouseover="showTip(event, 'fs23', 158)" class="rt">Function</span><span class="pn">></span><span class="pn">)</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs28', 159)" onmouseover="showTip(event, 'fs28', 159)" class="id">d</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs4', 160)" onmouseover="showTip(event, 'fs4', 160)" class="rt">Dag</span><span class="pn">)</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs55', 161)" onmouseover="showTip(event, 'fs55', 161)" class="d">Task</span><span class="pn"><</span><span class="ta">'</span><span class="id">a</span><span class="pn">></span> <span class="o">=</span>
<span class="k">downcast</span> <span onmouseout="hideTip(event, 'fs28', 162)" onmouseover="showTip(event, 'fs28', 162)" class="id">d</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs12', 163)" onmouseover="showTip(event, 'fs12', 163)" class="id">FunctionValues</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs32', 164)" onmouseover="showTip(event, 'fs32', 164)" class="id">i</span><span class="pn">]</span><span class="pn">.</span><span class="id">Value</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs56', 165)" onmouseover="showTip(event, 'fs56', 165)" class="fn">changed</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs25', 166)" onmouseover="showTip(event, 'fs25', 166)" class="uc">Cell</span> <span onmouseout="hideTip(event, 'fs32', 167)" onmouseover="showTip(event, 'fs32', 167)" class="id">i</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs25', 168)" onmouseover="showTip(event, 'fs25', 168)" class="rt">Cell</span><span class="pn"><</span><span class="ta">'</span><span class="id">a</span><span class="pn">,</span><span class="ta">'</span><span class="id">t</span><span class="pn">></span><span class="pn">)</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs57', 169)" onmouseover="showTip(event, 'fs57', 169)" class="id">before</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs4', 170)" onmouseover="showTip(event, 'fs4', 170)" class="rt">Dag</span><span class="pn">)</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs58', 171)" onmouseover="showTip(event, 'fs58', 171)" class="id">after</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs4', 172)" onmouseover="showTip(event, 'fs4', 172)" class="rt">Dag</span><span class="pn">)</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs59', 173)" onmouseover="showTip(event, 'fs59', 173)" class="vt">bool</span> <span class="o">=</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs60', 174)" onmouseover="showTip(event, 'fs60', 174)" class="k">typeof</span><span class="pn"><</span><span class="ta">'</span><span class="id">t</span><span class="pn">></span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs60', 175)" onmouseover="showTip(event, 'fs60', 175)" class="k">typeof</span><span class="pn"><</span><span onmouseout="hideTip(event, 'fs23', 176)" onmouseover="showTip(event, 'fs23', 176)" class="rt">Function</span><span class="pn">></span> <span class="k">then</span>
<span onmouseout="hideTip(event, 'fs57', 177)" onmouseover="showTip(event, 'fs57', 177)" class="id">before</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs12', 178)" onmouseover="showTip(event, 'fs12', 178)" class="id">FunctionValues</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs32', 179)" onmouseover="showTip(event, 'fs32', 179)" class="id">i</span><span class="pn">]</span> <span class="o"><></span> <span onmouseout="hideTip(event, 'fs58', 180)" onmouseover="showTip(event, 'fs58', 180)" class="id">after</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs12', 181)" onmouseover="showTip(event, 'fs12', 181)" class="id">FunctionValues</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs32', 182)" onmouseover="showTip(event, 'fs32', 182)" class="id">i</span><span class="pn">]</span>
<span class="k">else</span>
<span class="k">downcast</span> <span onmouseout="hideTip(event, 'fs57', 183)" onmouseover="showTip(event, 'fs57', 183)" class="id">before</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs5', 184)" onmouseover="showTip(event, 'fs5', 184)" class="id">InputValues</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs32', 185)" onmouseover="showTip(event, 'fs32', 185)" class="id">i</span><span class="pn">]</span> <span class="o"><></span> <span class="k">downcast</span> <span onmouseout="hideTip(event, 'fs58', 186)" onmouseover="showTip(event, 'fs58', 186)" class="id">after</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs5', 187)" onmouseover="showTip(event, 'fs5', 187)" class="id">InputValues</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs32', 188)" onmouseover="showTip(event, 'fs32', 188)" class="id">i</span><span class="pn">]</span>
<span class="k">type</span> <span class="ta">'</span><span class="id">a</span> <span onmouseout="hideTip(event, 'fs61', 189)" onmouseover="showTip(event, 'fs61', 189)" class="rt">Builder</span> <span class="o">=</span> <span class="k">private</span> <span class="pn">{</span>
<span onmouseout="hideTip(event, 'fs62', 190)" onmouseover="showTip(event, 'fs62', 190)" class="id">Dag</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs4', 191)" onmouseover="showTip(event, 'fs4', 191)" class="rt">Dag</span>
<span onmouseout="hideTip(event, 'fs63', 192)" onmouseover="showTip(event, 'fs63', 192)" class="id">Inputs</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs9', 193)" onmouseover="showTip(event, 'fs9', 193)" class="rt">Set</span><span class="pn"><</span><span onmouseout="hideTip(event, 'fs10', 194)" onmouseover="showTip(event, 'fs10', 194)" class="vt">int</span><span class="pn">></span> <span class="pn">*</span> <span onmouseout="hideTip(event, 'fs9', 195)" onmouseover="showTip(event, 'fs9', 195)" class="rt">Set</span><span class="pn"><</span><span onmouseout="hideTip(event, 'fs10', 196)" onmouseover="showTip(event, 'fs10', 196)" class="vt">int</span><span class="pn">></span>
<span onmouseout="hideTip(event, 'fs64', 197)" onmouseover="showTip(event, 'fs64', 197)" class="fn">Function</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs4', 198)" onmouseover="showTip(event, 'fs4', 198)" class="rt">Dag</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs55', 199)" onmouseover="showTip(event, 'fs55', 199)" class="d">Task</span><span class="pn"><</span><span class="ta">'</span><span class="id">a</span><span class="pn">></span>
<span class="pn">}</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs65', 200)" onmouseover="showTip(event, 'fs65', 200)" class="fn">buildFunction</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs28', 201)" onmouseover="showTip(event, 'fs28', 201)" class="id">d</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs4', 202)" onmouseover="showTip(event, 'fs4', 202)" class="rt">Dag</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs66', 203)" onmouseover="showTip(event, 'fs66', 203)" class="id">f</span> <span class="o">=</span> <span class="pn">{</span>
<span onmouseout="hideTip(event, 'fs4', 204)" onmouseover="showTip(event, 'fs4', 204)" class="id">Dag</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs28', 205)" onmouseover="showTip(event, 'fs28', 205)" class="id">d</span>
<span class="id">Inputs</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs9', 206)" onmouseover="showTip(event, 'fs9', 206)" class="m">Set</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs47', 207)" onmouseover="showTip(event, 'fs47', 207)" class="id">empty</span><span class="pn">,</span> <span onmouseout="hideTip(event, 'fs9', 208)" onmouseover="showTip(event, 'fs9', 208)" class="m">Set</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs47', 209)" onmouseover="showTip(event, 'fs47', 209)" class="id">empty</span>
<span onmouseout="hideTip(event, 'fs23', 210)" onmouseover="showTip(event, 'fs23', 210)" class="fn">Function</span> <span class="o">=</span> <span class="k">fun</span> <span class="id">_</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs55', 211)" onmouseover="showTip(event, 'fs55', 211)" class="d">Task</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs67', 212)" onmouseover="showTip(event, 'fs67', 212)" class="id">FromResult</span> <span onmouseout="hideTip(event, 'fs66', 213)" onmouseover="showTip(event, 'fs66', 213)" class="id">f</span>
<span class="pn">}</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs68', 214)" onmouseover="showTip(event, 'fs68', 214)" class="fn">applyCell</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs25', 215)" onmouseover="showTip(event, 'fs25', 215)" class="uc">Cell</span> <span onmouseout="hideTip(event, 'fs32', 216)" onmouseover="showTip(event, 'fs32', 216)" class="id">i</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs25', 217)" onmouseover="showTip(event, 'fs25', 217)" class="rt">Cell</span><span class="pn"><</span><span class="ta">'</span><span class="id">a</span><span class="pn">,</span><span class="ta">'</span><span class="id">t</span><span class="pn">></span><span class="pn">)</span> <span class="pn">{</span><span onmouseout="hideTip(event, 'fs4', 218)" onmouseover="showTip(event, 'fs4', 218)" class="id">Dag</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs52', 219)" onmouseover="showTip(event, 'fs52', 219)" class="id">dag</span><span class="pn">;</span><span class="id">Inputs</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs69', 220)" onmouseover="showTip(event, 'fs69', 220)" class="id">inI</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs70', 221)" onmouseover="showTip(event, 'fs70', 221)" class="id">inC</span><span class="pn">;</span><span onmouseout="hideTip(event, 'fs23', 222)" onmouseover="showTip(event, 'fs23', 222)" class="fn">Function</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs71', 223)" onmouseover="showTip(event, 'fs71', 223)" class="fn">bFn</span><span class="pn">}</span> <span class="o">=</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs72', 224)" onmouseover="showTip(event, 'fs72', 224)" class="fn">taskMap</span> <span onmouseout="hideTip(event, 'fs73', 225)" onmouseover="showTip(event, 'fs73', 225)" class="fn">f</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs74', 226)" onmouseover="showTip(event, 'fs74', 226)" class="id">t</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs55', 227)" onmouseover="showTip(event, 'fs55', 227)" class="d">Task</span><span class="pn"><</span><span class="id">_</span><span class="pn">></span><span class="pn">)</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs74', 228)" onmouseover="showTip(event, 'fs74', 228)" class="fn">t</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs75', 229)" onmouseover="showTip(event, 'fs75', 229)" class="id">ContinueWith</span><span class="pn">(</span><span class="k">fun</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs76', 230)" onmouseover="showTip(event, 'fs76', 230)" class="id">r</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs55', 231)" onmouseover="showTip(event, 'fs55', 231)" class="d">Task</span><span class="pn"><</span><span class="id">_</span><span class="pn">></span><span class="pn">)</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs73', 232)" onmouseover="showTip(event, 'fs73', 232)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs76', 233)" onmouseover="showTip(event, 'fs76', 233)" class="id">r</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs77', 234)" onmouseover="showTip(event, 'fs77', 234)" class="id">Result</span><span class="pn">)</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs78', 235)" onmouseover="showTip(event, 'fs78', 235)" class="id">isFunctionCell</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs60', 236)" onmouseover="showTip(event, 'fs60', 236)" class="k">typeof</span><span class="pn"><</span><span class="ta">'</span><span class="id">t</span><span class="pn">></span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs60', 237)" onmouseover="showTip(event, 'fs60', 237)" class="k">typeof</span><span class="pn"><</span><span onmouseout="hideTip(event, 'fs23', 238)" onmouseover="showTip(event, 'fs23', 238)" class="rt">Function</span><span class="pn">></span>
<span class="pn">{</span>
<span onmouseout="hideTip(event, 'fs4', 239)" onmouseover="showTip(event, 'fs4', 239)" class="id">Dag</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs52', 240)" onmouseover="showTip(event, 'fs52', 240)" class="id">dag</span>
<span class="id">Inputs</span> <span class="o">=</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs78', 241)" onmouseover="showTip(event, 'fs78', 241)" class="id">isFunctionCell</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs69', 242)" onmouseover="showTip(event, 'fs69', 242)" class="id">inI</span><span class="pn">,</span> <span onmouseout="hideTip(event, 'fs9', 243)" onmouseover="showTip(event, 'fs9', 243)" class="m">Set</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs46', 244)" onmouseover="showTip(event, 'fs46', 244)" class="id">add</span> <span onmouseout="hideTip(event, 'fs32', 245)" onmouseover="showTip(event, 'fs32', 245)" class="id">i</span> <span onmouseout="hideTip(event, 'fs70', 246)" onmouseover="showTip(event, 'fs70', 246)" class="id">inC</span>
<span class="k">else</span> <span onmouseout="hideTip(event, 'fs9', 247)" onmouseover="showTip(event, 'fs9', 247)" class="m">Set</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs46', 248)" onmouseover="showTip(event, 'fs46', 248)" class="id">add</span> <span onmouseout="hideTip(event, 'fs32', 249)" onmouseover="showTip(event, 'fs32', 249)" class="id">i</span> <span onmouseout="hideTip(event, 'fs69', 250)" onmouseover="showTip(event, 'fs69', 250)" class="id">inI</span><span class="pn">,</span> <span onmouseout="hideTip(event, 'fs70', 251)" onmouseover="showTip(event, 'fs70', 251)" class="id">inC</span>
<span onmouseout="hideTip(event, 'fs23', 252)" onmouseover="showTip(event, 'fs23', 252)" class="fn">Function</span> <span class="o">=</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs78', 253)" onmouseover="showTip(event, 'fs78', 253)" class="id">isFunctionCell</span> <span class="k">then</span>
<span class="k">fun</span> <span onmouseout="hideTip(event, 'fs28', 254)" onmouseover="showTip(event, 'fs28', 254)" class="id">d</span> <span class="k">-></span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs79', 255)" onmouseover="showTip(event, 'fs79', 255)" class="id">fTask</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs71', 256)" onmouseover="showTip(event, 'fs71', 256)" class="fn">bFn</span> <span onmouseout="hideTip(event, 'fs28', 257)" onmouseover="showTip(event, 'fs28', 257)" class="id">d</span>
<span class="fn">(</span> <span class="k">downcast</span> <span onmouseout="hideTip(event, 'fs28', 258)" onmouseover="showTip(event, 'fs28', 258)" class="id">d</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs12', 259)" onmouseover="showTip(event, 'fs12', 259)" class="id">FunctionValues</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs32', 260)" onmouseover="showTip(event, 'fs32', 260)" class="id">i</span><span class="pn">]</span><span class="pn">.</span><span class="id">Value</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs72', 261)" onmouseover="showTip(event, 'fs72', 261)" class="fn">taskMap</span> <span class="pn">(</span><span class="k">fun</span> <span onmouseout="hideTip(event, 'fs80', 262)" onmouseover="showTip(event, 'fs80', 262)" class="id">a</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs72', 263)" onmouseover="showTip(event, 'fs72', 263)" class="fn">taskMap</span> <span class="pn">(</span><span class="k">fun</span> <span onmouseout="hideTip(event, 'fs81', 264)" onmouseover="showTip(event, 'fs81', 264)" class="fn">f</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs81', 265)" onmouseover="showTip(event, 'fs81', 265)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs80', 266)" onmouseover="showTip(event, 'fs80', 266)" class="id">a</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs79', 267)" onmouseover="showTip(event, 'fs79', 267)" class="id">fTask</span><span class="pn">)</span>
<span class="pn">)</span><span class="pn">.</span><span class="id">Unwrap</span><span class="pn">(</span><span class="pn">)</span>
<span class="k">else</span>
<span class="k">fun</span> <span onmouseout="hideTip(event, 'fs28', 268)" onmouseover="showTip(event, 'fs28', 268)" class="id">d</span> <span class="k">-></span>
<span onmouseout="hideTip(event, 'fs71', 269)" onmouseover="showTip(event, 'fs71', 269)" class="fn">bFn</span> <span onmouseout="hideTip(event, 'fs28', 270)" onmouseover="showTip(event, 'fs28', 270)" class="id">d</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs72', 271)" onmouseover="showTip(event, 'fs72', 271)" class="fn">taskMap</span> <span class="pn">(</span><span class="k">fun</span> <span onmouseout="hideTip(event, 'fs81', 272)" onmouseover="showTip(event, 'fs81', 272)" class="fn">f</span> <span class="k">-></span>
<span class="k">downcast</span> <span onmouseout="hideTip(event, 'fs28', 273)" onmouseover="showTip(event, 'fs28', 273)" class="id">d</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs5', 274)" onmouseover="showTip(event, 'fs5', 274)" class="id">InputValues</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs32', 275)" onmouseover="showTip(event, 'fs32', 275)" class="id">i</span><span class="pn">]</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs81', 276)" onmouseover="showTip(event, 'fs81', 276)" class="fn">f</span> <span class="pn">)</span>
<span class="pn">}</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs82', 277)" onmouseover="showTip(event, 'fs82', 277)" class="fn">addFunction</span> <span class="pn">(</span><span class="pn">{</span><span onmouseout="hideTip(event, 'fs4', 278)" onmouseover="showTip(event, 'fs4', 278)" class="id">Dag</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs52', 279)" onmouseover="showTip(event, 'fs52', 279)" class="id">dag</span><span class="pn">;</span><span class="id">Inputs</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs83', 280)" onmouseover="showTip(event, 'fs83', 280)" class="id">ips</span><span class="pn">;</span><span onmouseout="hideTip(event, 'fs23', 281)" onmouseover="showTip(event, 'fs23', 281)" class="fn">Function</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs84', 282)" onmouseover="showTip(event, 'fs84', 282)" class="fn">fn</span><span class="pn">}</span><span class="pn">:</span><span class="ta">'</span><span class="id">a</span> <span onmouseout="hideTip(event, 'fs61', 283)" onmouseover="showTip(event, 'fs61', 283)" class="rt">Builder</span><span class="pn">)</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs85', 284)" onmouseover="showTip(event, 'fs85', 284)" class="fn">calc</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs84', 285)" onmouseover="showTip(event, 'fs84', 285)" class="fn">fn</span> <span class="o">></span><span class="pn">></span> <span onmouseout="hideTip(event, 'fs29', 286)" onmouseover="showTip(event, 'fs29', 286)" class="fn">box</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs28', 287)" onmouseover="showTip(event, 'fs28', 287)" class="id">d</span> <span class="o">=</span> <span class="pn">{</span>
<span onmouseout="hideTip(event, 'fs52', 288)" onmouseover="showTip(event, 'fs52', 288)" class="id">dag</span> <span class="k">with</span>
<span class="id">FunctionInputs</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs14', 289)" onmouseover="showTip(event, 'fs14', 289)" class="fn">append</span> <span onmouseout="hideTip(event, 'fs52', 290)" onmouseover="showTip(event, 'fs52', 290)" class="id">dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs8', 291)" onmouseover="showTip(event, 'fs8', 291)" class="id">FunctionInputs</span> <span onmouseout="hideTip(event, 'fs83', 292)" onmouseover="showTip(event, 'fs83', 292)" class="id">ips</span>
<span class="id">FunctionFunctions</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs14', 293)" onmouseover="showTip(event, 'fs14', 293)" class="fn">append</span> <span onmouseout="hideTip(event, 'fs52', 294)" onmouseover="showTip(event, 'fs52', 294)" class="id">dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs11', 295)" onmouseover="showTip(event, 'fs11', 295)" class="id">FunctionFunctions</span> <span onmouseout="hideTip(event, 'fs85', 296)" onmouseover="showTip(event, 'fs85', 296)" class="fn">calc</span>
<span class="id">FunctionValues</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs14', 297)" onmouseover="showTip(event, 'fs14', 297)" class="fn">append</span> <span onmouseout="hideTip(event, 'fs52', 298)" onmouseover="showTip(event, 'fs52', 298)" class="id">dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs12', 299)" onmouseover="showTip(event, 'fs12', 299)" class="id">FunctionValues</span> <span class="k">null</span>
<span class="pn">}</span>
<span onmouseout="hideTip(event, 'fs28', 300)" onmouseover="showTip(event, 'fs28', 300)" class="id">d</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs12', 301)" onmouseover="showTip(event, 'fs12', 301)" class="id">FunctionValues</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs28', 302)" onmouseover="showTip(event, 'fs28', 302)" class="id">d</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs12', 303)" onmouseover="showTip(event, 'fs12', 303)" class="id">FunctionValues</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs30', 304)" onmouseover="showTip(event, 'fs30', 304)" class="id">Length</span><span class="o">-</span><span class="n">1</span><span class="pn">]</span> <span class="k"><-</span> <span class="k">lazy</span> <span onmouseout="hideTip(event, 'fs85', 305)" onmouseover="showTip(event, 'fs85', 305)" class="fn">calc</span> <span onmouseout="hideTip(event, 'fs28', 306)" onmouseover="showTip(event, 'fs28', 306)" class="id">d</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs86', 307)" onmouseover="showTip(event, 'fs86', 307)" class="id">cell</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs25', 308)" onmouseover="showTip(event, 'fs25', 308)" class="rt">Cell</span><span class="pn"><</span><span class="ta">'</span><span class="id">a</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs23', 309)" onmouseover="showTip(event, 'fs23', 309)" class="rt">Function</span><span class="pn">></span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs25', 310)" onmouseover="showTip(event, 'fs25', 310)" class="uc">Cell</span> <span onmouseout="hideTip(event, 'fs52', 311)" onmouseover="showTip(event, 'fs52', 311)" class="id">dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs12', 312)" onmouseover="showTip(event, 'fs12', 312)" class="id">FunctionValues</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs30', 313)" onmouseover="showTip(event, 'fs30', 313)" class="id">Length</span>
<span onmouseout="hideTip(event, 'fs28', 314)" onmouseover="showTip(event, 'fs28', 314)" class="id">d</span><span class="pn">,</span> <span onmouseout="hideTip(event, 'fs86', 315)" onmouseover="showTip(event, 'fs86', 315)" class="id">cell</span>
</code></pre>
<h2><a name="Testing" class="anchor" href="#Testing">Testing</a></h2>
<p>The following tests demonstrate use of the DAG.</p>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="k">let</span> <span class="id">tests</span> <span class="o">=</span>
<span class="id">testList</span> <span class="s">"dag tests"</span> <span class="pn">[</span>
<span class="id">testAsync</span> <span class="s">"one cell"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell1</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 317)" onmouseover="showTip(event, 'fs88', 317)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 318)" onmouseover="showTip(event, 'fs27', 318)" class="id">addInput</span> <span class="n">7</span> <span onmouseout="hideTip(event, 'fs88', 319)" onmouseover="showTip(event, 'fs88', 319)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs26', 320)" onmouseover="showTip(event, 'fs26', 320)" class="id">empty</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="n">7</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs88', 321)" onmouseover="showTip(event, 'fs88', 321)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs31', 322)" onmouseover="showTip(event, 'fs31', 322)" class="id">getValue</span> <span class="id">cell1</span> <span class="id">dag</span><span class="pn">)</span> <span class="s">"one cell"</span>
<span class="pn">}</span>
<span class="id">testAsync</span> <span class="s">"two cell"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell1</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 323)" onmouseover="showTip(event, 'fs88', 323)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 324)" onmouseover="showTip(event, 'fs27', 324)" class="id">addInput</span> <span class="n">8</span> <span onmouseout="hideTip(event, 'fs88', 325)" onmouseover="showTip(event, 'fs88', 325)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs26', 326)" onmouseover="showTip(event, 'fs26', 326)" class="id">empty</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell2</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 327)" onmouseover="showTip(event, 'fs88', 327)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 328)" onmouseover="showTip(event, 'fs27', 328)" class="id">addInput</span> <span class="n">9</span> <span class="id">dag</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="n">8</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs88', 329)" onmouseover="showTip(event, 'fs88', 329)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs31', 330)" onmouseover="showTip(event, 'fs31', 330)" class="id">getValue</span> <span class="id">cell1</span> <span class="id">dag</span><span class="pn">)</span> <span class="s">"first 8"</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="n">9</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs88', 331)" onmouseover="showTip(event, 'fs88', 331)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs31', 332)" onmouseover="showTip(event, 'fs31', 332)" class="id">getValue</span> <span class="id">cell2</span> <span class="id">dag</span><span class="pn">)</span> <span class="s">"second 9"</span>
<span class="pn">}</span>
<span class="id">testAsync</span> <span class="s">"one function"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell1</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 333)" onmouseover="showTip(event, 'fs88', 333)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 334)" onmouseover="showTip(event, 'fs27', 334)" class="id">addInput</span> <span class="n">42</span> <span onmouseout="hideTip(event, 'fs88', 335)" onmouseover="showTip(event, 'fs88', 335)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs26', 336)" onmouseover="showTip(event, 'fs26', 336)" class="id">empty</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell2</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs88', 337)" onmouseover="showTip(event, 'fs88', 337)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs65', 338)" onmouseover="showTip(event, 'fs65', 338)" class="id">buildFunction</span> <span class="id">dag</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">x</span> <span class="k">-></span> <span class="id">x</span> <span class="pn">*</span> <span class="n">10</span><span class="pn">)</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 339)" onmouseover="showTip(event, 'fs88', 339)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs68', 340)" onmouseover="showTip(event, 'fs68', 340)" class="id">applyCell</span> <span class="id">cell1</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 341)" onmouseover="showTip(event, 'fs88', 341)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs82', 342)" onmouseover="showTip(event, 'fs82', 342)" class="id">addFunction</span>
<span class="k">let!</span> <span class="id">result</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 343)" onmouseover="showTip(event, 'fs88', 343)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs54', 344)" onmouseover="showTip(event, 'fs54', 344)" class="id">getValueTask</span> <span class="id">cell2</span> <span class="id">dag</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs89', 345)" onmouseover="showTip(event, 'fs89', 345)" class="id">Async</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs90', 346)" onmouseover="showTip(event, 'fs90', 346)" class="id">AwaitTask</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="n">420</span> <span class="id">result</span> <span class="s">"42 * 10 = 420"</span>
<span class="pn">}</span>
<span class="id">testAsync</span> <span class="s">"one function with set"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell1</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 347)" onmouseover="showTip(event, 'fs88', 347)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 348)" onmouseover="showTip(event, 'fs27', 348)" class="id">addInput</span> <span class="n">13</span> <span onmouseout="hideTip(event, 'fs88', 349)" onmouseover="showTip(event, 'fs88', 349)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs26', 350)" onmouseover="showTip(event, 'fs26', 350)" class="id">empty</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell2</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs88', 351)" onmouseover="showTip(event, 'fs88', 351)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs65', 352)" onmouseover="showTip(event, 'fs65', 352)" class="id">buildFunction</span> <span class="id">dag</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">x</span> <span class="k">-></span> <span class="id">x</span> <span class="pn">*</span> <span class="n">10</span><span class="pn">)</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 353)" onmouseover="showTip(event, 'fs88', 353)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs68', 354)" onmouseover="showTip(event, 'fs68', 354)" class="id">applyCell</span> <span class="id">cell1</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 355)" onmouseover="showTip(event, 'fs88', 355)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs82', 356)" onmouseover="showTip(event, 'fs82', 356)" class="id">addFunction</span>
<span class="k">let</span> <span class="id">dag</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 357)" onmouseover="showTip(event, 'fs88', 357)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs33', 358)" onmouseover="showTip(event, 'fs33', 358)" class="id">setInput</span> <span class="id">cell1</span> <span class="n">43</span> <span class="id">dag</span>
<span class="k">let!</span> <span class="id">result</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 359)" onmouseover="showTip(event, 'fs88', 359)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs54', 360)" onmouseover="showTip(event, 'fs54', 360)" class="id">getValueTask</span> <span class="id">cell2</span> <span class="id">dag</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs89', 361)" onmouseover="showTip(event, 'fs89', 361)" class="id">Async</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs90', 362)" onmouseover="showTip(event, 'fs90', 362)" class="id">AwaitTask</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="n">430</span> <span class="id">result</span> <span class="s">"43 * 10 = 430"</span>
<span class="pn">}</span>
<span class="id">testAsync</span> <span class="s">"one function with set twice"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell1</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 363)" onmouseover="showTip(event, 'fs88', 363)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 364)" onmouseover="showTip(event, 'fs27', 364)" class="id">addInput</span> <span class="n">15</span> <span onmouseout="hideTip(event, 'fs88', 365)" onmouseover="showTip(event, 'fs88', 365)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs26', 366)" onmouseover="showTip(event, 'fs26', 366)" class="id">empty</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell2</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs88', 367)" onmouseover="showTip(event, 'fs88', 367)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs65', 368)" onmouseover="showTip(event, 'fs65', 368)" class="id">buildFunction</span> <span class="id">dag</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">x</span> <span class="k">-></span> <span class="id">x</span> <span class="pn">*</span> <span class="n">10</span><span class="pn">)</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 369)" onmouseover="showTip(event, 'fs88', 369)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs68', 370)" onmouseover="showTip(event, 'fs68', 370)" class="id">applyCell</span> <span class="id">cell1</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 371)" onmouseover="showTip(event, 'fs88', 371)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs82', 372)" onmouseover="showTip(event, 'fs82', 372)" class="id">addFunction</span>
<span class="k">let</span> <span class="id">dag</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 373)" onmouseover="showTip(event, 'fs88', 373)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs33', 374)" onmouseover="showTip(event, 'fs33', 374)" class="id">setInput</span> <span class="id">cell1</span> <span class="n">43</span> <span class="id">dag</span>
<span class="k">let</span> <span class="id">dag</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 375)" onmouseover="showTip(event, 'fs88', 375)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs33', 376)" onmouseover="showTip(event, 'fs33', 376)" class="id">setInput</span> <span class="id">cell1</span> <span class="n">44</span> <span class="id">dag</span>
<span class="k">let!</span> <span class="id">result</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 377)" onmouseover="showTip(event, 'fs88', 377)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs54', 378)" onmouseover="showTip(event, 'fs54', 378)" class="id">getValueTask</span> <span class="id">cell2</span> <span class="id">dag</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs89', 379)" onmouseover="showTip(event, 'fs89', 379)" class="id">Async</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs90', 380)" onmouseover="showTip(event, 'fs90', 380)" class="id">AwaitTask</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="n">440</span> <span class="id">result</span> <span class="s">"44 * 10 = 440"</span>
<span class="pn">}</span>
<span class="id">testAsync</span> <span class="s">"not changed input"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">dagBefore</span><span class="pn">,</span> <span class="id">cell1</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 381)" onmouseover="showTip(event, 'fs88', 381)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 382)" onmouseover="showTip(event, 'fs27', 382)" class="id">addInput</span> <span class="n">42</span> <span onmouseout="hideTip(event, 'fs88', 383)" onmouseover="showTip(event, 'fs88', 383)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs26', 384)" onmouseover="showTip(event, 'fs26', 384)" class="id">empty</span>
<span class="k">let</span> <span class="id">dagAfter</span><span class="pn">,</span><span class="id">_</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 385)" onmouseover="showTip(event, 'fs88', 385)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 386)" onmouseover="showTip(event, 'fs27', 386)" class="id">addInput</span> <span class="n">45</span> <span class="id">dagBefore</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">isFalse</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs88', 387)" onmouseover="showTip(event, 'fs88', 387)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs56', 388)" onmouseover="showTip(event, 'fs56', 388)" class="id">changed</span> <span class="id">cell1</span> <span class="id">dagBefore</span> <span class="id">dagAfter</span><span class="pn">)</span> <span class="s">"no change"</span>
<span class="pn">}</span>
<span class="id">testAsync</span> <span class="s">"changed input"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">dagBefore</span><span class="pn">,</span> <span class="id">cell1</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 389)" onmouseover="showTip(event, 'fs88', 389)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 390)" onmouseover="showTip(event, 'fs27', 390)" class="id">addInput</span> <span class="n">42</span> <span onmouseout="hideTip(event, 'fs88', 391)" onmouseover="showTip(event, 'fs88', 391)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs26', 392)" onmouseover="showTip(event, 'fs26', 392)" class="id">empty</span>
<span class="k">let</span> <span class="id">dagAfter</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 393)" onmouseover="showTip(event, 'fs88', 393)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs33', 394)" onmouseover="showTip(event, 'fs33', 394)" class="id">setInput</span> <span class="id">cell1</span> <span class="n">45</span> <span class="id">dagBefore</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">isTrue</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs88', 395)" onmouseover="showTip(event, 'fs88', 395)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs56', 396)" onmouseover="showTip(event, 'fs56', 396)" class="id">changed</span> <span class="id">cell1</span> <span class="id">dagBefore</span> <span class="id">dagAfter</span><span class="pn">)</span> <span class="s">"changed"</span>
<span class="pn">}</span>
<span class="id">testAsync</span> <span class="s">"not changed function"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell1</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 397)" onmouseover="showTip(event, 'fs88', 397)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 398)" onmouseover="showTip(event, 'fs27', 398)" class="id">addInput</span> <span class="n">42</span> <span onmouseout="hideTip(event, 'fs88', 399)" onmouseover="showTip(event, 'fs88', 399)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs26', 400)" onmouseover="showTip(event, 'fs26', 400)" class="id">empty</span>
<span class="k">let</span> <span class="id">dagBefore</span><span class="pn">,</span> <span class="id">cell2</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs88', 401)" onmouseover="showTip(event, 'fs88', 401)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs65', 402)" onmouseover="showTip(event, 'fs65', 402)" class="id">buildFunction</span> <span class="id">dag</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">x</span> <span class="k">-></span> <span class="id">x</span> <span class="pn">*</span> <span class="n">10</span><span class="pn">)</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 403)" onmouseover="showTip(event, 'fs88', 403)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs68', 404)" onmouseover="showTip(event, 'fs68', 404)" class="id">applyCell</span> <span class="id">cell1</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 405)" onmouseover="showTip(event, 'fs88', 405)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs82', 406)" onmouseover="showTip(event, 'fs82', 406)" class="id">addFunction</span>
<span class="k">let</span> <span class="id">dagAfter</span><span class="pn">,</span><span class="id">_</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 407)" onmouseover="showTip(event, 'fs88', 407)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 408)" onmouseover="showTip(event, 'fs27', 408)" class="id">addInput</span> <span class="n">45</span> <span class="id">dagBefore</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">isFalse</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs88', 409)" onmouseover="showTip(event, 'fs88', 409)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs56', 410)" onmouseover="showTip(event, 'fs56', 410)" class="id">changed</span> <span class="id">cell2</span> <span class="id">dagBefore</span> <span class="id">dagAfter</span><span class="pn">)</span> <span class="s">"no change"</span>
<span class="pn">}</span>
<span class="id">testAsync</span> <span class="s">"changed function"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell1</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 411)" onmouseover="showTip(event, 'fs88', 411)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 412)" onmouseover="showTip(event, 'fs27', 412)" class="id">addInput</span> <span class="n">17</span> <span onmouseout="hideTip(event, 'fs88', 413)" onmouseover="showTip(event, 'fs88', 413)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs26', 414)" onmouseover="showTip(event, 'fs26', 414)" class="id">empty</span>
<span class="k">let</span> <span class="id">dagBefore</span><span class="pn">,</span> <span class="id">cell2</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs88', 415)" onmouseover="showTip(event, 'fs88', 415)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs65', 416)" onmouseover="showTip(event, 'fs65', 416)" class="id">buildFunction</span> <span class="id">dag</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">x</span> <span class="k">-></span> <span class="id">x</span> <span class="pn">*</span> <span class="n">10</span><span class="pn">)</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 417)" onmouseover="showTip(event, 'fs88', 417)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs68', 418)" onmouseover="showTip(event, 'fs68', 418)" class="id">applyCell</span> <span class="id">cell1</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 419)" onmouseover="showTip(event, 'fs88', 419)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs82', 420)" onmouseover="showTip(event, 'fs82', 420)" class="id">addFunction</span>
<span class="k">let</span> <span class="id">dagAfter</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 421)" onmouseover="showTip(event, 'fs88', 421)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs33', 422)" onmouseover="showTip(event, 'fs33', 422)" class="id">setInput</span> <span class="id">cell1</span> <span class="n">45</span> <span class="id">dagBefore</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">isTrue</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs88', 423)" onmouseover="showTip(event, 'fs88', 423)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs56', 424)" onmouseover="showTip(event, 'fs56', 424)" class="id">changed</span> <span class="id">cell2</span> <span class="id">dagBefore</span> <span class="id">dagAfter</span><span class="pn">)</span> <span class="s">"changed"</span>
<span class="k">let!</span> <span class="id">result</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 425)" onmouseover="showTip(event, 'fs88', 425)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs54', 426)" onmouseover="showTip(event, 'fs54', 426)" class="id">getValueTask</span> <span class="id">cell2</span> <span class="id">dagAfter</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs89', 427)" onmouseover="showTip(event, 'fs89', 427)" class="id">Async</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs90', 428)" onmouseover="showTip(event, 'fs90', 428)" class="id">AwaitTask</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="n">450</span> <span class="id">result</span> <span class="s">"45 * 10 = 450"</span>
<span class="k">let!</span> <span class="id">result</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 429)" onmouseover="showTip(event, 'fs88', 429)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs54', 430)" onmouseover="showTip(event, 'fs54', 430)" class="id">getValueTask</span> <span class="id">cell2</span> <span class="id">dagBefore</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs89', 431)" onmouseover="showTip(event, 'fs89', 431)" class="id">Async</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs90', 432)" onmouseover="showTip(event, 'fs90', 432)" class="id">AwaitTask</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="n">170</span> <span class="id">result</span> <span class="s">"17 * 10 = 170"</span>
<span class="pn">}</span>
<span class="id">testAsync</span> <span class="s">"chained functions"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell1</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 433)" onmouseover="showTip(event, 'fs88', 433)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 434)" onmouseover="showTip(event, 'fs27', 434)" class="id">addInput</span> <span class="n">18</span> <span onmouseout="hideTip(event, 'fs88', 435)" onmouseover="showTip(event, 'fs88', 435)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs26', 436)" onmouseover="showTip(event, 'fs26', 436)" class="id">empty</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell2</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs88', 437)" onmouseover="showTip(event, 'fs88', 437)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs65', 438)" onmouseover="showTip(event, 'fs65', 438)" class="id">buildFunction</span> <span class="id">dag</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">x</span> <span class="k">-></span> <span class="id">x</span> <span class="pn">*</span> <span class="n">10</span><span class="pn">)</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 439)" onmouseover="showTip(event, 'fs88', 439)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs68', 440)" onmouseover="showTip(event, 'fs68', 440)" class="id">applyCell</span> <span class="id">cell1</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 441)" onmouseover="showTip(event, 'fs88', 441)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs82', 442)" onmouseover="showTip(event, 'fs82', 442)" class="id">addFunction</span>
<span class="k">let</span> <span class="id">dagBefore</span><span class="pn">,</span> <span class="id">cell3</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs88', 443)" onmouseover="showTip(event, 'fs88', 443)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs65', 444)" onmouseover="showTip(event, 'fs65', 444)" class="id">buildFunction</span> <span class="id">dag</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">x</span> <span class="k">-></span> <span class="id">x</span> <span class="o">+</span> <span class="n">1</span><span class="pn">)</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 445)" onmouseover="showTip(event, 'fs88', 445)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs68', 446)" onmouseover="showTip(event, 'fs68', 446)" class="id">applyCell</span> <span class="id">cell2</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 447)" onmouseover="showTip(event, 'fs88', 447)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs82', 448)" onmouseover="showTip(event, 'fs82', 448)" class="id">addFunction</span>
<span class="k">let</span> <span class="id">dagAfter</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 449)" onmouseover="showTip(event, 'fs88', 449)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs33', 450)" onmouseover="showTip(event, 'fs33', 450)" class="id">setInput</span> <span class="id">cell1</span> <span class="n">23</span> <span class="id">dagBefore</span>
<span class="k">let!</span> <span class="id">result</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 451)" onmouseover="showTip(event, 'fs88', 451)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs54', 452)" onmouseover="showTip(event, 'fs54', 452)" class="id">getValueTask</span> <span class="id">cell3</span> <span class="id">dagAfter</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs89', 453)" onmouseover="showTip(event, 'fs89', 453)" class="id">Async</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs90', 454)" onmouseover="showTip(event, 'fs90', 454)" class="id">AwaitTask</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="n">231</span> <span class="id">result</span> <span class="s">"231"</span>
<span class="k">let!</span> <span class="id">result</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 455)" onmouseover="showTip(event, 'fs88', 455)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs54', 456)" onmouseover="showTip(event, 'fs54', 456)" class="id">getValueTask</span> <span class="id">cell3</span> <span class="id">dagBefore</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs89', 457)" onmouseover="showTip(event, 'fs89', 457)" class="id">Async</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs90', 458)" onmouseover="showTip(event, 'fs90', 458)" class="id">AwaitTask</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="n">181</span> <span class="id">result</span> <span class="s">"181"</span>
<span class="pn">}</span>
<span class="id">testAsync</span> <span class="s">"two function"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell1</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 459)" onmouseover="showTip(event, 'fs88', 459)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 460)" onmouseover="showTip(event, 'fs27', 460)" class="id">addInput</span> <span class="s">"z"</span> <span onmouseout="hideTip(event, 'fs88', 461)" onmouseover="showTip(event, 'fs88', 461)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs26', 462)" onmouseover="showTip(event, 'fs26', 462)" class="id">empty</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell2</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 463)" onmouseover="showTip(event, 'fs88', 463)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 464)" onmouseover="showTip(event, 'fs27', 464)" class="id">addInput</span> <span class="n">7</span> <span class="id">dag</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell3</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs88', 465)" onmouseover="showTip(event, 'fs88', 465)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs65', 466)" onmouseover="showTip(event, 'fs65', 466)" class="id">buildFunction</span> <span class="id">dag</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">s</span> <span class="pn">(</span><span class="id">n</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs10', 467)" onmouseover="showTip(event, 'fs10', 467)" class="id">int</span><span class="pn">)</span> <span class="k">-></span> <span class="id">s</span> <span class="o">+</span> <span onmouseout="hideTip(event, 'fs91', 468)" onmouseover="showTip(event, 'fs91', 468)" class="id">string</span> <span class="id">n</span><span class="pn">)</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 469)" onmouseover="showTip(event, 'fs88', 469)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs68', 470)" onmouseover="showTip(event, 'fs68', 470)" class="id">applyCell</span> <span class="id">cell1</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 471)" onmouseover="showTip(event, 'fs88', 471)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs68', 472)" onmouseover="showTip(event, 'fs68', 472)" class="id">applyCell</span> <span class="id">cell2</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 473)" onmouseover="showTip(event, 'fs88', 473)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs82', 474)" onmouseover="showTip(event, 'fs82', 474)" class="id">addFunction</span>
<span class="k">let!</span> <span class="id">result</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 475)" onmouseover="showTip(event, 'fs88', 475)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs54', 476)" onmouseover="showTip(event, 'fs54', 476)" class="id">getValueTask</span> <span class="id">cell3</span> <span class="id">dag</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs89', 477)" onmouseover="showTip(event, 'fs89', 477)" class="id">Async</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs90', 478)" onmouseover="showTip(event, 'fs90', 478)" class="id">AwaitTask</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="s">"z7"</span> <span class="id">result</span> <span class="s">"z7"</span>
<span class="pn">}</span>
<span class="id">testAsync</span> <span class="s">"three function with set"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell1</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 479)" onmouseover="showTip(event, 'fs88', 479)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 480)" onmouseover="showTip(event, 'fs27', 480)" class="id">addInput</span> <span class="s">"f"</span> <span onmouseout="hideTip(event, 'fs88', 481)" onmouseover="showTip(event, 'fs88', 481)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs26', 482)" onmouseover="showTip(event, 'fs26', 482)" class="id">empty</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell2</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 483)" onmouseover="showTip(event, 'fs88', 483)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 484)" onmouseover="showTip(event, 'fs27', 484)" class="id">addInput</span> <span class="n">8</span> <span class="id">dag</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell3</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 485)" onmouseover="showTip(event, 'fs88', 485)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 486)" onmouseover="showTip(event, 'fs27', 486)" class="id">addInput</span> <span class="n">1.5</span> <span class="id">dag</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell4</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs88', 487)" onmouseover="showTip(event, 'fs88', 487)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs65', 488)" onmouseover="showTip(event, 'fs65', 488)" class="id">buildFunction</span> <span class="id">dag</span>
<span class="pn">(</span><span class="k">fun</span> <span class="id">s</span> <span class="pn">(</span><span class="id">n</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs10', 489)" onmouseover="showTip(event, 'fs10', 489)" class="id">int</span><span class="pn">)</span> <span class="pn">(</span><span class="id">f</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs92', 490)" onmouseover="showTip(event, 'fs92', 490)" class="id">float</span><span class="pn">)</span> <span class="k">-></span> <span class="id">s</span> <span class="o">+</span> <span onmouseout="hideTip(event, 'fs91', 491)" onmouseover="showTip(event, 'fs91', 491)" class="id">string</span> <span class="id">n</span> <span class="o">+</span> <span class="s">"-"</span> <span class="o">+</span> <span onmouseout="hideTip(event, 'fs91', 492)" onmouseover="showTip(event, 'fs91', 492)" class="id">string</span> <span class="id">f</span><span class="pn">)</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 493)" onmouseover="showTip(event, 'fs88', 493)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs68', 494)" onmouseover="showTip(event, 'fs68', 494)" class="id">applyCell</span> <span class="id">cell1</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 495)" onmouseover="showTip(event, 'fs88', 495)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs68', 496)" onmouseover="showTip(event, 'fs68', 496)" class="id">applyCell</span> <span class="id">cell2</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 497)" onmouseover="showTip(event, 'fs88', 497)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs68', 498)" onmouseover="showTip(event, 'fs68', 498)" class="id">applyCell</span> <span class="id">cell3</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 499)" onmouseover="showTip(event, 'fs88', 499)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs82', 500)" onmouseover="showTip(event, 'fs82', 500)" class="id">addFunction</span>
<span class="k">let!</span> <span class="id">result</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 501)" onmouseover="showTip(event, 'fs88', 501)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs54', 502)" onmouseover="showTip(event, 'fs54', 502)" class="id">getValueTask</span> <span class="id">cell4</span> <span class="id">dag</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs89', 503)" onmouseover="showTip(event, 'fs89', 503)" class="id">Async</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs90', 504)" onmouseover="showTip(event, 'fs90', 504)" class="id">AwaitTask</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="s">"f8-1.5"</span> <span class="id">result</span> <span class="s">"f8-1.5"</span>
<span class="k">let</span> <span class="id">dag</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 505)" onmouseover="showTip(event, 'fs88', 505)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs33', 506)" onmouseover="showTip(event, 'fs33', 506)" class="id">setInput</span> <span class="id">cell1</span> <span class="s">"w"</span> <span class="id">dag</span>
<span class="k">let!</span> <span class="id">result</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 507)" onmouseover="showTip(event, 'fs88', 507)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs54', 508)" onmouseover="showTip(event, 'fs54', 508)" class="id">getValueTask</span> <span class="id">cell4</span> <span class="id">dag</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs89', 509)" onmouseover="showTip(event, 'fs89', 509)" class="id">Async</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs90', 510)" onmouseover="showTip(event, 'fs90', 510)" class="id">AwaitTask</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="s">"w8-1.5"</span> <span class="id">result</span> <span class="s">"w8-1.5"</span>
<span class="pn">}</span>
<span class="id">testAsync</span> <span class="s">"chained functions multi"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell1</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 511)" onmouseover="showTip(event, 'fs88', 511)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 512)" onmouseover="showTip(event, 'fs27', 512)" class="id">addInput</span> <span class="s">"a"</span> <span onmouseout="hideTip(event, 'fs88', 513)" onmouseover="showTip(event, 'fs88', 513)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs26', 514)" onmouseover="showTip(event, 'fs26', 514)" class="id">empty</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell2</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 515)" onmouseover="showTip(event, 'fs88', 515)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 516)" onmouseover="showTip(event, 'fs27', 516)" class="id">addInput</span> <span class="n">1</span> <span class="id">dag</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell3</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs88', 517)" onmouseover="showTip(event, 'fs88', 517)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs65', 518)" onmouseover="showTip(event, 'fs65', 518)" class="id">buildFunction</span> <span class="id">dag</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">s</span> <span class="pn">(</span><span class="id">n</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs10', 519)" onmouseover="showTip(event, 'fs10', 519)" class="id">int</span><span class="pn">)</span> <span class="k">-></span> <span class="s">"x:"</span> <span class="o">+</span> <span class="id">s</span> <span class="o">+</span> <span onmouseout="hideTip(event, 'fs91', 520)" onmouseover="showTip(event, 'fs91', 520)" class="id">string</span> <span class="id">n</span><span class="pn">)</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 521)" onmouseover="showTip(event, 'fs88', 521)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs68', 522)" onmouseover="showTip(event, 'fs68', 522)" class="id">applyCell</span> <span class="id">cell1</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 523)" onmouseover="showTip(event, 'fs88', 523)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs68', 524)" onmouseover="showTip(event, 'fs68', 524)" class="id">applyCell</span> <span class="id">cell2</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 525)" onmouseover="showTip(event, 'fs88', 525)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs82', 526)" onmouseover="showTip(event, 'fs82', 526)" class="id">addFunction</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell4</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 527)" onmouseover="showTip(event, 'fs88', 527)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 528)" onmouseover="showTip(event, 'fs27', 528)" class="id">addInput</span> <span class="s">"b"</span> <span class="id">dag</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell5</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 529)" onmouseover="showTip(event, 'fs88', 529)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 530)" onmouseover="showTip(event, 'fs27', 530)" class="id">addInput</span> <span class="n">2</span> <span class="id">dag</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell6</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs88', 531)" onmouseover="showTip(event, 'fs88', 531)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs65', 532)" onmouseover="showTip(event, 'fs65', 532)" class="id">buildFunction</span> <span class="id">dag</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">s</span> <span class="pn">(</span><span class="id">n</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs10', 533)" onmouseover="showTip(event, 'fs10', 533)" class="id">int</span><span class="pn">)</span> <span class="k">-></span> <span class="s">"y:"</span> <span class="o">+</span> <span class="id">s</span> <span class="o">+</span> <span onmouseout="hideTip(event, 'fs91', 534)" onmouseover="showTip(event, 'fs91', 534)" class="id">string</span> <span class="id">n</span><span class="pn">)</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 535)" onmouseover="showTip(event, 'fs88', 535)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs68', 536)" onmouseover="showTip(event, 'fs68', 536)" class="id">applyCell</span> <span class="id">cell4</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 537)" onmouseover="showTip(event, 'fs88', 537)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs68', 538)" onmouseover="showTip(event, 'fs68', 538)" class="id">applyCell</span> <span class="id">cell5</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 539)" onmouseover="showTip(event, 'fs88', 539)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs82', 540)" onmouseover="showTip(event, 'fs82', 540)" class="id">addFunction</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell7</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 541)" onmouseover="showTip(event, 'fs88', 541)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 542)" onmouseover="showTip(event, 'fs27', 542)" class="id">addInput</span> <span class="s">"c"</span> <span class="id">dag</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell8</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 543)" onmouseover="showTip(event, 'fs88', 543)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 544)" onmouseover="showTip(event, 'fs27', 544)" class="id">addInput</span> <span class="n">3</span> <span class="id">dag</span>
<span class="k">let</span> <span class="id">dag</span><span class="pn">,</span> <span class="id">cell9</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs88', 545)" onmouseover="showTip(event, 'fs88', 545)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs65', 546)" onmouseover="showTip(event, 'fs65', 546)" class="id">buildFunction</span> <span class="id">dag</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">s</span> <span class="pn">(</span><span class="id">n</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs10', 547)" onmouseover="showTip(event, 'fs10', 547)" class="id">int</span><span class="pn">)</span> <span class="k">-></span> <span class="s">"z:"</span> <span class="o">+</span> <span class="id">s</span> <span class="o">+</span> <span onmouseout="hideTip(event, 'fs91', 548)" onmouseover="showTip(event, 'fs91', 548)" class="id">string</span> <span class="id">n</span><span class="pn">)</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 549)" onmouseover="showTip(event, 'fs88', 549)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs68', 550)" onmouseover="showTip(event, 'fs68', 550)" class="id">applyCell</span> <span class="id">cell7</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 551)" onmouseover="showTip(event, 'fs88', 551)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs68', 552)" onmouseover="showTip(event, 'fs68', 552)" class="id">applyCell</span> <span class="id">cell8</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 553)" onmouseover="showTip(event, 'fs88', 553)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs82', 554)" onmouseover="showTip(event, 'fs82', 554)" class="id">addFunction</span>
<span class="k">let</span> <span class="id">dagBefore</span><span class="pn">,</span> <span class="id">cell10</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs88', 555)" onmouseover="showTip(event, 'fs88', 555)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs65', 556)" onmouseover="showTip(event, 'fs65', 556)" class="id">buildFunction</span> <span class="id">dag</span>
<span class="pn">(</span><span class="k">fun</span> <span class="id">s1</span> <span class="id">s2</span> <span class="id">s3</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs93', 557)" onmouseover="showTip(event, 'fs93', 557)" class="id">String</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs94', 558)" onmouseover="showTip(event, 'fs94', 558)" class="id">concat</span> <span class="s">"|"</span> <span class="pn">[</span><span class="id">s1</span><span class="pn">;</span><span class="id">s2</span><span class="pn">;</span><span class="id">s3</span><span class="pn">]</span><span class="pn">)</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 559)" onmouseover="showTip(event, 'fs88', 559)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs68', 560)" onmouseover="showTip(event, 'fs68', 560)" class="id">applyCell</span> <span class="id">cell3</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 561)" onmouseover="showTip(event, 'fs88', 561)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs68', 562)" onmouseover="showTip(event, 'fs68', 562)" class="id">applyCell</span> <span class="id">cell6</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 563)" onmouseover="showTip(event, 'fs88', 563)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs68', 564)" onmouseover="showTip(event, 'fs68', 564)" class="id">applyCell</span> <span class="id">cell9</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs88', 565)" onmouseover="showTip(event, 'fs88', 565)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs82', 566)" onmouseover="showTip(event, 'fs82', 566)" class="id">addFunction</span>
<span class="k">let</span> <span class="id">dagAfter</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 567)" onmouseover="showTip(event, 'fs88', 567)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs33', 568)" onmouseover="showTip(event, 'fs33', 568)" class="id">setInput</span> <span class="id">cell5</span> <span class="n">4</span> <span class="id">dagBefore</span>
<span class="k">let!</span> <span class="id">result</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 569)" onmouseover="showTip(event, 'fs88', 569)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs54', 570)" onmouseover="showTip(event, 'fs54', 570)" class="id">getValueTask</span> <span class="id">cell10</span> <span class="id">dagAfter</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs89', 571)" onmouseover="showTip(event, 'fs89', 571)" class="id">Async</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs90', 572)" onmouseover="showTip(event, 'fs90', 572)" class="id">AwaitTask</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="s">"x:a1|y:b4|z:c3"</span> <span class="id">result</span> <span class="s">"x:a1|y:b4|z:c3"</span>
<span class="k">let!</span> <span class="id">result</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs88', 573)" onmouseover="showTip(event, 'fs88', 573)" class="id">Dag</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs54', 574)" onmouseover="showTip(event, 'fs54', 574)" class="id">getValueTask</span> <span class="id">cell10</span> <span class="id">dagBefore</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs89', 575)" onmouseover="showTip(event, 'fs89', 575)" class="id">Async</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs90', 576)" onmouseover="showTip(event, 'fs90', 576)" class="id">AwaitTask</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="s">"x:a1|y:b2|z:c3"</span> <span class="id">result</span> <span class="s">"x:a1|y:b2|z:c3"</span>
<span class="pn">}</span>
<span class="pn">]</span>
</code></pre>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p>This has been a very successful experiment.
The DAG has some nice features while keeping type safety.</p>
<p>The way immutability has been implemented means it is probably not best suited to fast realtime updates or very fine-grained calculations.
For more coarse-grained calculations like grids of dependent fields, where each cell represents a column of values and summaries, it could be ideal.</p>
<div class="tip" id="fs1">namespace System</div>
<div class="tip" id="fs2">namespace System.Threading</div>
<div class="tip" id="fs3">namespace System.Threading.Tasks</div>
<div class="tip" id="fs4">type Dag =<br />  private {InputValues: obj array;<br />           FunctionInputs: (Set<int> * Set<int>) array;<br />           FunctionFunctions: (Dag -> obj) array;<br />           FunctionValues: Lazy<obj> array;}</div>
<div class="tip" id="fs5">Dag.InputValues: obj array</div>
<div class="tip" id="fs6">type obj = Object</div>
<div class="tip" id="fs7">type 'T array = 'T []</div>
<div class="tip" id="fs8">Dag.FunctionInputs: (Set<int> * Set<int>) array</div>
<div class="tip" id="fs9">Multiple items<br />module Set<br /><br />from Microsoft.FSharp.Collections<br /><br />--------------------<br />type Set<'T (requires comparison)> =<br />  interface IReadOnlyCollection<'T><br />  interface IComparable<br />  interface IEnumerable<br />  interface IEnumerable<'T><br />  interface ICollection<'T><br />  new : elements:seq<'T> -> Set<'T><br />  member Add : value:'T -> Set<'T><br />  member Contains : value:'T -> bool<br />  override Equals : obj -> bool<br />  member IsProperSubsetOf : otherSet:Set<'T> -> bool<br />  ...<br /><br />--------------------<br />new : elements:seq<'T> -> Set<'T></div>
<div class="tip" id="fs10">Multiple items<br />val int : value:'T -> int (requires member op_Explicit)<br /><br />--------------------<br />type int = int32<br /><br />--------------------<br />type int<'Measure> = int</div>
<div class="tip" id="fs11">Dag.FunctionFunctions: (Dag -> obj) array</div>
<div class="tip" id="fs12">Dag.FunctionValues: Lazy<obj> array</div>
<div class="tip" id="fs13">Multiple items<br />active recognizer Lazy: Lazy<'T> -> 'T<br /><br />--------------------<br />type Lazy<'T> =<br />  new : unit -> Lazy<'T> + 5 overloads<br />  member IsValueCreated : bool<br />  member ToString : unit -> string<br />  member Value : 'T<br /><br />--------------------<br />type Lazy<'T,'TMetadata> =<br />  inherit Lazy<'T><br />  new : metadata:'TMetadata -> Lazy<'T, 'TMetadata> + 5 overloads<br />  member Metadata : 'TMetadata<br /><br />--------------------<br />Lazy() : Lazy<'T><br />Lazy(valueFactory: Func<'T>) : Lazy<'T><br />Lazy(isThreadSafe: bool) : Lazy<'T><br />Lazy(mode: Threading.LazyThreadSafetyMode) : Lazy<'T><br />Lazy(valueFactory: Func<'T>, isThreadSafe: bool) : Lazy<'T><br />Lazy(valueFactory: Func<'T>, mode: Threading.LazyThreadSafetyMode) : Lazy<'T><br /><br />--------------------<br />Lazy(metadata: 'TMetadata) : Lazy<'T,'TMetadata><br />Lazy(valueFactory: Func<'T>, metadata: 'TMetadata) : Lazy<'T,'TMetadata><br />Lazy(metadata: 'TMetadata, isThreadSafe: bool) : Lazy<'T,'TMetadata><br />Lazy(metadata: 'TMetadata, mode: Threading.LazyThreadSafetyMode) : Lazy<'T,'TMetadata><br />Lazy(valueFactory: Func<'T>, metadata: 'TMetadata, isThreadSafe: bool) : Lazy<'T,'TMetadata><br />Lazy(valueFactory: Func<'T>, metadata: 'TMetadata, mode: Threading.LazyThreadSafetyMode) : Lazy<'T,'TMetadata></div>
<div class="tip" id="fs14">val private append : a:'a [] -> v:'a -> 'a []</div>
<div class="tip" id="fs15">val a : 'a []</div>
<div class="tip" id="fs16">val v : 'a</div>
<div class="tip" id="fs17">val mutable a : 'a []</div>
<div class="tip" id="fs18">type Array =<br />  member Clone : unit -> obj<br />  member CopyTo : array:Array * index:int -> unit + 1 overload<br />  member GetEnumerator : unit -> IEnumerator<br />  member GetLength : dimension:int -> int<br />  member GetLongLength : dimension:int -> int64<br />  member GetLowerBound : dimension:int -> int<br />  member GetUpperBound : dimension:int -> int<br />  member GetValue : [<ParamArray>] indices:int[] -> obj + 7 overloads<br />  member Initialize : unit -> unit<br />  member IsFixedSize : bool<br />  ...</div>
<div class="tip" id="fs19">Array.Resize<'T>(array: byref<'T []>, newSize: int) : unit</div>
<div class="tip" id="fs20">val length : array:'T [] -> int</div>
<div class="tip" id="fs21">type Input = private | CellInput</div>
<div class="tip" id="fs22">union case Dag.Input.CellInput: Dag.Input</div>
<div class="tip" id="fs23">type Function = private | CellFunction</div>
<div class="tip" id="fs24">union case Dag.Function.CellFunction: Dag.Function</div>
<div class="tip" id="fs25">Multiple items<br />union case Dag.Cell.Cell: int -> Dag.Cell<'a,'b><br /><br />--------------------<br />type Cell<'a,'b> = private | Cell of int</div>
<div class="tip" id="fs26">val empty : Dag</div>
<div class="tip" id="fs27">val addInput : v:'a -> d:Dag -> Dag * Dag.Cell<'a,Dag.Input></div>
<div class="tip" id="fs28">val d : Dag</div>
<div class="tip" id="fs29">val box : value:'T -> obj</div>
<div class="tip" id="fs30">property Array.Length: int</div>
<div class="tip" id="fs31">val getValue : Dag.Cell<'a,Dag.Input> -> d:Dag -> 'a</div>
<div class="tip" id="fs32">val i : int</div>
<div class="tip" id="fs33">val setInput : Dag.Cell<'a,Dag.Input> -> a:'a -> d:Dag -> Dag (requires equality)</div>
<div class="tip" id="fs34">val a : 'a (requires equality)</div>
<div class="tip" id="fs35">val dirtyCalcs : Set<int></div>
<div class="tip" id="fs36">module Seq<br /><br />from Microsoft.FSharp.Collections</div>
<div class="tip" id="fs37">val fold : folder:('State -> 'T -> 'State) -> state:'State -> source:seq<'T> -> 'State</div>
<div class="tip" id="fs38">val j : int</div>
<div class="tip" id="fs39">val s : Set<int></div>
<div class="tip" id="fs40">val inputs : Set<int></div>
<div class="tip" id="fs41">val calcInputs : Set<int></div>
<div class="tip" id="fs42">val contains : element:'T -> set:Set<'T> -> bool (requires comparison)</div>
<div class="tip" id="fs43">val intersect : set1:Set<'T> -> set2:Set<'T> -> Set<'T> (requires comparison)</div>
<div class="tip" id="fs44">val isEmpty : set:Set<'T> -> bool (requires comparison)</div>
<div class="tip" id="fs45">val not : value:bool -> bool</div>
<div class="tip" id="fs46">val add : value:'T -> set:Set<'T> -> Set<'T> (requires comparison)</div>
<div class="tip" id="fs47">val empty<'T (requires comparison)> : Set<'T> (requires comparison)</div>
<div class="tip" id="fs48">val snd : tuple:('T1 * 'T2) -> 'T2</div>
<div class="tip" id="fs49">val inputValues : obj []</div>
<div class="tip" id="fs50">val copy : array:'T [] -> 'T []</div>
<div class="tip" id="fs51">val functionValues : Lazy<obj> []</div>
<div class="tip" id="fs52">val dag : Dag</div>
<div class="tip" id="fs53">val iter : action:('T -> unit) -> set:Set<'T> -> unit (requires comparison)</div>
<div class="tip" id="fs54">val getValueTask : Dag.Cell<'a,Dag.Function> -> d:Dag -> Task<'a></div>
<div class="tip" id="fs55">Multiple items<br />type Task =<br />  new : action:Action -> Task + 7 overloads<br />  member AsyncState : obj<br />  member ConfigureAwait : continueOnCapturedContext:bool -> ConfiguredTaskAwaitable<br />  member ContinueWith : continuationAction:Action<Task> -> Task + 19 overloads<br />  member CreationOptions : TaskCreationOptions<br />  member Dispose : unit -> unit<br />  member Exception : AggregateException<br />  member GetAwaiter : unit -> TaskAwaiter<br />  member Id : int<br />  member IsCanceled : bool<br />  ...<br /><br />--------------------<br />type Task<'TResult> =<br />  inherit Task<br />  new : function:Func<'TResult> -> Task<'TResult> + 7 overloads<br />  member ConfigureAwait : continueOnCapturedContext:bool -> ConfiguredTaskAwaitable<'TResult><br />  member ContinueWith : continuationAction:Action<Task<'TResult>> -> Task + 19 overloads<br />  member GetAwaiter : unit -> TaskAwaiter<'TResult><br />  member Result : 'TResult<br />  static member Factory : TaskFactory<'TResult><br /><br />--------------------<br />Task(action: Action) : Task<br />Task(action: Action, cancellationToken: Threading.CancellationToken) : Task<br />Task(action: Action, creationOptions: TaskCreationOptions) : Task<br />Task(action: Action<obj>, state: obj) : Task<br />Task(action: Action, cancellationToken: Threading.CancellationToken, creationOptions: TaskCreationOptions) : Task<br />Task(action: Action<obj>, state: obj, cancellationToken: Threading.CancellationToken) : Task<br />Task(action: Action<obj>, state: obj, creationOptions: TaskCreationOptions) : Task<br />Task(action: Action<obj>, state: obj, cancellationToken: Threading.CancellationToken, creationOptions: TaskCreationOptions) : Task<br /><br />--------------------<br />Task(function: Func<'TResult>) : Task<'TResult><br />Task(function: Func<'TResult>, cancellationToken: Threading.CancellationToken) : Task<'TResult><br />Task(function: Func<'TResult>, creationOptions: TaskCreationOptions) : Task<'TResult><br />Task(function: Func<obj,'TResult>, state: obj) : Task<'TResult><br />Task(function: Func<'TResult>, cancellationToken: Threading.CancellationToken, creationOptions: TaskCreationOptions) : Task<'TResult><br />Task(function: Func<obj,'TResult>, state: obj, cancellationToken: Threading.CancellationToken) : Task<'TResult><br />Task(function: Func<obj,'TResult>, state: obj, creationOptions: TaskCreationOptions) : Task<'TResult><br />Task(function: Func<obj,'TResult>, state: obj, cancellationToken: Threading.CancellationToken, creationOptions: TaskCreationOptions) : Task<'TResult></div>
<div class="tip" id="fs56">val changed : Dag.Cell<'a,'t> -> before:Dag -> after:Dag -> bool</div>
<div class="tip" id="fs57">val before : Dag</div>
<div class="tip" id="fs58">val after : Dag</div>
<div class="tip" id="fs59">type bool = Boolean</div>
<div class="tip" id="fs60">val typeof<'T> : Type</div>
<div class="tip" id="fs61">type 'a Builder =<br />  private {Dag: Dag;<br />           Inputs: Set<int> * Set<int>;<br />           Function: Dag -> Task<'a>;}</div>
<div class="tip" id="fs62">Multiple items<br />Dag.Builder.Dag: Dag<br /><br />--------------------<br />type Dag =<br />  private {InputValues: obj array;<br />           FunctionInputs: (Set<int> * Set<int>) array;<br />           FunctionFunctions: (Dag -> obj) array;<br />           FunctionValues: Lazy<obj> array;}</div>
<div class="tip" id="fs63">Dag.Builder.Inputs: Set<int> * Set<int></div>
<div class="tip" id="fs64">Multiple items<br />Dag.Builder.Function: Dag -> Task<'a><br /><br />--------------------<br />type Function = private | CellFunction</div>
<div class="tip" id="fs65">val buildFunction : d:Dag -> f:'a -> 'a Dag.Builder</div>
<div class="tip" id="fs66">val f : 'a</div>
<div class="tip" id="fs67">Task.FromResult<'TResult>(result: 'TResult) : Task<'TResult></div>
<div class="tip" id="fs68">val applyCell : Dag.Cell<'a,'t> -> ('a0 -> 'b) Dag.Builder -> 'b Dag.Builder</div>
<div class="tip" id="fs69">val inI : Set<int></div>
<div class="tip" id="fs70">val inC : Set<int></div>
<div class="tip" id="fs71">val bFn : (Dag -> Task<('a -> 'b)>)</div>
<div class="tip" id="fs72">val taskMap : (('c -> 'd) -> Task<'c> -> Task<'d>)</div>
<div class="tip" id="fs73">val f : ('c -> 'd)</div>
<div class="tip" id="fs74">val t : Task<'c></div>
<div class="tip" id="fs75">Task.ContinueWith<'TResult>(continuationFunction: Func<Task,'TResult>) : Task<'TResult><br />   <em>(+0 other overloads)</em><br />Task.ContinueWith(continuationAction: Action<Task>) : Task<br />   <em>(+0 other overloads)</em><br />Task.ContinueWith<'TNewResult>(continuationFunction: Func<Task<'c>,'TNewResult>) : Task<'TNewResult><br />   <em>(+0 other overloads)</em><br />Task.ContinueWith(continuationAction: Action<Task<'c>>) : Task<br />   <em>(+0 other overloads)</em><br />Task.ContinueWith<'TResult>(continuationFunction: Func<Task,obj,'TResult>, state: obj) : Task<'TResult><br />   <em>(+0 other overloads)</em><br />Task.ContinueWith<'TResult>(continuationFunction: Func<Task,'TResult>, continuationOptions: TaskContinuationOptions) : Task<'TResult><br />   <em>(+0 other overloads)</em><br />Task.ContinueWith<'TResult>(continuationFunction: Func<Task,'TResult>, scheduler: TaskScheduler) : Task<'TResult><br />   <em>(+0 other overloads)</em><br />Task.ContinueWith<'TResult>(continuationFunction: Func<Task,'TResult>, cancellationToken: Threading.CancellationToken) : Task<'TResult><br />   <em>(+0 other overloads)</em><br />Task.ContinueWith(continuationAction: Action<Task,obj>, state: obj) : Task<br />   <em>(+0 other overloads)</em><br />Task.ContinueWith(continuationAction: Action<Task>, continuationOptions: TaskContinuationOptions) : Task<br />   <em>(+0 other overloads)</em></div>
<div class="tip" id="fs76">val r : Task<'c></div>
<div class="tip" id="fs77">property Task.Result: 'c</div>
<div class="tip" id="fs78">val isFunctionCell : bool</div>
<div class="tip" id="fs79">val fTask : Task<('a -> 'b)></div>
<div class="tip" id="fs80">val a : 'a</div>
<div class="tip" id="fs81">val f : ('a -> 'b)</div>
<div class="tip" id="fs82">val addFunction : 'a Dag.Builder -> Dag * Dag.Cell<'a,Dag.Function></div>
<div class="tip" id="fs83">val ips : Set<int> * Set<int></div>
<div class="tip" id="fs84">val fn : (Dag -> Task<'a>)</div>
<div class="tip" id="fs85">val calc : (Dag -> obj)</div>
<div class="tip" id="fs86">val cell : Dag.Cell<'a,Dag.Function></div>
<div class="tip" id="fs87">val x : int</div>
<div class="tip" id="fs88">Multiple items<br />module Dag<br /><br />from DagBlog<br /><br />--------------------<br />type Dag =<br />  private {InputValues: obj array;<br />           FunctionInputs: (Set<int> * Set<int>) array;<br />           FunctionFunctions: (Dag -> obj) array;<br />           FunctionValues: Lazy<obj> array;}</div>
<div class="tip" id="fs89">Multiple items<br />type Async =<br />  static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)<br />  static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)<br />  static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool><br />  static member AwaitTask : task:Task -> Async<unit><br />  static member AwaitTask : task:Task<'T> -> Async<'T><br />  static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool><br />  static member CancelDefaultToken : unit -> unit<br />  static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>><br />  static member Choice : computations:seq<Async<'T option>> -> Async<'T option><br />  static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T><br />  ...<br /><br />--------------------<br />type Async<'T> =</div>
<div class="tip" id="fs90">static member Async.AwaitTask : task:Task -> Async<unit><br />static member Async.AwaitTask : task:Task<'T> -> Async<'T></div>
<div class="tip" id="fs91">Multiple items<br />val string : value:'T -> string<br /><br />--------------------<br />type string = String</div>
<div class="tip" id="fs92">Multiple items<br />val float : value:'T -> float (requires member op_Explicit)<br /><br />--------------------<br />type float = Double<br /><br />--------------------<br />type float<'Measure> = float</div>
<div class="tip" id="fs93">Multiple items<br />type String =<br />  new : value:char -> string + 7 overloads<br />  member Chars : int -> char<br />  member Clone : unit -> obj<br />  member CompareTo : value:obj -> int + 1 overload<br />  member Contains : value:string -> bool<br />  member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit<br />  member EndsWith : value:string -> bool + 2 overloads<br />  member Equals : obj:obj -> bool + 2 overloads<br />  member GetEnumerator : unit -> CharEnumerator<br />  member GetHashCode : unit -> int<br />  ...<br /><br />--------------------<br />String(value: nativeptr<char>) : String<br />String(value: nativeptr<sbyte>) : String<br />String(value: char []) : String<br />String(c: char, count: int) : String<br />String(value: nativeptr<char>, startIndex: int, length: int) : String<br />String(value: nativeptr<sbyte>, startIndex: int, length: int) : String<br />String(value: char [], startIndex: int, length: int) : String<br />String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: Text.Encoding) : String</div>
<div class="tip" id="fs94">val concat : sep:string -> strings:seq<string> -> string</div>
Rounding Algorithms from Property Based Testing
2018-03-05T00:00:00+00:00
http://anthonylloyd.github.io/blog/2018/03/05/rounding
<p>Steffen Forkmann recently posted a <a href="http://www.navision-blog.de/blog/2018/02/21/rounding-is-a-bitch/">blog</a> about incorrect rounding in a twitter poll, and how coding a rounding strategy is a hard problem.
This got me thinking about how many correct rounding algorithms there are.</p>
<p>In these type of problems it is important to look at what properties the algorithm should have.
Property based testing is a great tool when doing this.</p>
<p>The key property required for a fair rounding algorithm is that rounded values increase with the weights.
It doesn't make sense for a lower weight to have a greater rounded value.
Symmetry in the results for negative weights and negative value to be distributed are also important.
This can easily be achieved by mapping from the positive results, but a robust algorithm shouldn't need to do this.</p>
<p>In the blog it was proposed that adjusting the largest weight would work, but in general this can only work when the rounding needs a positive adjustment due to the increasing with weight property.
For negative adjustments the smallest non-zero weight would need to be adjusted.
It may also be unfair to adjust these values if they already have a large rounding error.</p>
<h2><a name="Error-minimisation-algorithm" class="anchor" href="#Error-minimisation-algorithm">Error minimisation algorithm</a></h2>
<p>This algorithm minimises the absolute and relative rounding errors.
I normally apply absolute then relative but the reverse order also works and may be more correct for certain problems.</p>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="c">/// Distribute integer n over an array of weights</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs3', 3)" onmouseover="showTip(event, 'fs3', 3)" class="fn">distribute</span> <span onmouseout="hideTip(event, 'fs4', 4)" onmouseover="showTip(event, 'fs4', 4)" class="id">n</span> <span onmouseout="hideTip(event, 'fs5', 5)" onmouseover="showTip(event, 'fs5', 5)" class="id">weights</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs6', 6)" onmouseover="showTip(event, 'fs6', 6)" class="id">wt</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs7', 7)" onmouseover="showTip(event, 'fs7', 7)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs8', 8)" onmouseover="showTip(event, 'fs8', 8)" class="id">sum</span> <span onmouseout="hideTip(event, 'fs5', 9)" onmouseover="showTip(event, 'fs5', 9)" class="id">weights</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs7', 10)" onmouseover="showTip(event, 'fs7', 10)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs9', 11)" onmouseover="showTip(event, 'fs9', 11)" class="id">isEmpty</span> <span onmouseout="hideTip(event, 'fs5', 12)" onmouseover="showTip(event, 'fs5', 12)" class="id">weights</span>
<span class="o">||</span> <span onmouseout="hideTip(event, 'fs10', 13)" onmouseover="showTip(event, 'fs10', 13)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs11', 14)" onmouseover="showTip(event, 'fs11', 14)" class="id">maxBy</span> <span onmouseout="hideTip(event, 'fs12', 15)" onmouseover="showTip(event, 'fs12', 15)" class="fn">abs</span> <span onmouseout="hideTip(event, 'fs5', 16)" onmouseover="showTip(event, 'fs5', 16)" class="id">weights</span> <span class="o">*</span> <span onmouseout="hideTip(event, 'fs13', 17)" onmouseover="showTip(event, 'fs13', 17)" class="fn">float</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs12', 18)" onmouseover="showTip(event, 'fs12', 18)" class="fn">abs</span> <span onmouseout="hideTip(event, 'fs4', 19)" onmouseover="showTip(event, 'fs4', 19)" class="id">n</span><span class="o">+</span><span class="n">1</span><span class="pn">)</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs12', 20)" onmouseover="showTip(event, 'fs12', 20)" class="fn">abs</span>
<span class="o">></span><span class="o">=</span> <span onmouseout="hideTip(event, 'fs12', 21)" onmouseover="showTip(event, 'fs12', 21)" class="fn">abs</span> <span onmouseout="hideTip(event, 'fs6', 22)" onmouseover="showTip(event, 'fs6', 22)" class="id">wt</span> <span class="o">*</span> <span onmouseout="hideTip(event, 'fs13', 23)" onmouseover="showTip(event, 'fs13', 23)" class="fn">float</span> <span onmouseout="hideTip(event, 'fs14', 24)" onmouseover="showTip(event, 'fs14', 24)" class="vt">Int32</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs15', 25)" onmouseover="showTip(event, 'fs15', 25)" class="id">MaxValue</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs16', 26)" onmouseover="showTip(event, 'fs16', 26)" class="uc">None</span>
<span class="k">else</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs17', 27)" onmouseover="showTip(event, 'fs17', 27)" class="fn">round</span> <span onmouseout="hideTip(event, 'fs18', 28)" onmouseover="showTip(event, 'fs18', 28)" class="id">f</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs19', 29)" onmouseover="showTip(event, 'fs19', 29)" class="fn">int</span><span class="pn">(</span><span class="k">if</span> <span onmouseout="hideTip(event, 'fs18', 30)" onmouseover="showTip(event, 'fs18', 30)" class="id">f</span><span class="o"><</span><span class="n">0.0</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs18', 31)" onmouseover="showTip(event, 'fs18', 31)" class="id">f</span><span class="o">-</span><span class="n">0.5</span> <span class="k">else</span> <span onmouseout="hideTip(event, 'fs18', 32)" onmouseover="showTip(event, 'fs18', 32)" class="id">f</span><span class="o">+</span><span class="n">0.5</span><span class="pn">)</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs20', 33)" onmouseover="showTip(event, 'fs20', 33)" class="id">ns</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs7', 34)" onmouseover="showTip(event, 'fs7', 34)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs21', 35)" onmouseover="showTip(event, 'fs21', 35)" class="id">map</span> <span class="pn">(</span><span class="o">(*)</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs13', 36)" onmouseover="showTip(event, 'fs13', 36)" class="fn">float</span> <span onmouseout="hideTip(event, 'fs4', 37)" onmouseover="showTip(event, 'fs4', 37)" class="id">n</span> <span class="o">/</span> <span onmouseout="hideTip(event, 'fs6', 38)" onmouseover="showTip(event, 'fs6', 38)" class="id">wt</span><span class="pn">)</span> <span class="o">></span><span class="pn">></span> <span onmouseout="hideTip(event, 'fs17', 39)" onmouseover="showTip(event, 'fs17', 39)" class="fn">round</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs5', 40)" onmouseover="showTip(event, 'fs5', 40)" class="id">weights</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs22', 41)" onmouseover="showTip(event, 'fs22', 41)" class="fn">absoluteError</span> <span onmouseout="hideTip(event, 'fs23', 42)" onmouseover="showTip(event, 'fs23', 42)" class="id">wi</span> <span onmouseout="hideTip(event, 'fs24', 43)" onmouseover="showTip(event, 'fs24', 43)" class="id">ni</span> <span onmouseout="hideTip(event, 'fs25', 44)" onmouseover="showTip(event, 'fs25', 44)" class="id">d</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs26', 45)" onmouseover="showTip(event, 'fs26', 45)" class="id">wc</span> <span class="o">=</span> <span class="n">0.5</span> <span class="o">*</span> <span onmouseout="hideTip(event, 'fs13', 46)" onmouseover="showTip(event, 'fs13', 46)" class="fn">float</span> <span onmouseout="hideTip(event, 'fs25', 47)" onmouseover="showTip(event, 'fs25', 47)" class="id">d</span> <span class="o">*</span> <span onmouseout="hideTip(event, 'fs6', 48)" onmouseover="showTip(event, 'fs6', 48)" class="id">wt</span> <span class="o">/</span> <span onmouseout="hideTip(event, 'fs13', 49)" onmouseover="showTip(event, 'fs13', 49)" class="fn">float</span> <span onmouseout="hideTip(event, 'fs4', 50)" onmouseover="showTip(event, 'fs4', 50)" class="id">n</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs27', 51)" onmouseover="showTip(event, 'fs27', 51)" class="id">wni</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs13', 52)" onmouseover="showTip(event, 'fs13', 52)" class="fn">float</span> <span onmouseout="hideTip(event, 'fs24', 53)" onmouseover="showTip(event, 'fs24', 53)" class="id">ni</span> <span class="o">*</span> <span onmouseout="hideTip(event, 'fs6', 54)" onmouseover="showTip(event, 'fs6', 54)" class="id">wt</span> <span class="o">/</span> <span onmouseout="hideTip(event, 'fs13', 55)" onmouseover="showTip(event, 'fs13', 55)" class="fn">float</span> <span onmouseout="hideTip(event, 'fs4', 56)" onmouseover="showTip(event, 'fs4', 56)" class="id">n</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs26', 57)" onmouseover="showTip(event, 'fs26', 57)" class="id">wc</span> <span class="o">></span> <span class="n">0.0</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs28', 58)" onmouseover="showTip(event, 'fs28', 58)" class="fn">min</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs27', 59)" onmouseover="showTip(event, 'fs27', 59)" class="id">wni</span><span class="o">-</span><span onmouseout="hideTip(event, 'fs23', 60)" onmouseover="showTip(event, 'fs23', 60)" class="id">wi</span><span class="pn">)</span> <span class="n">0.0</span> <span class="o">+</span> <span onmouseout="hideTip(event, 'fs26', 61)" onmouseover="showTip(event, 'fs26', 61)" class="id">wc</span>
<span class="k">else</span> <span onmouseout="hideTip(event, 'fs28', 62)" onmouseover="showTip(event, 'fs28', 62)" class="fn">min</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs23', 63)" onmouseover="showTip(event, 'fs23', 63)" class="id">wi</span><span class="o">-</span><span onmouseout="hideTip(event, 'fs27', 64)" onmouseover="showTip(event, 'fs27', 64)" class="id">wni</span><span class="pn">)</span> <span class="n">0.0</span> <span class="o">-</span> <span onmouseout="hideTip(event, 'fs26', 65)" onmouseover="showTip(event, 'fs26', 65)" class="id">wc</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs29', 66)" onmouseover="showTip(event, 'fs29', 66)" class="id">d</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs4', 67)" onmouseover="showTip(event, 'fs4', 67)" class="id">n</span> <span class="o">-</span> <span onmouseout="hideTip(event, 'fs7', 68)" onmouseover="showTip(event, 'fs7', 68)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs8', 69)" onmouseover="showTip(event, 'fs8', 69)" class="id">sum</span> <span onmouseout="hideTip(event, 'fs20', 70)" onmouseover="showTip(event, 'fs20', 70)" class="id">ns</span>
<span class="k">for</span> <span onmouseout="hideTip(event, 'fs30', 71)" onmouseover="showTip(event, 'fs30', 71)" class="id">__</span> <span class="o">=</span> <span class="n">1</span> <span class="k">to</span> <span onmouseout="hideTip(event, 'fs12', 72)" onmouseover="showTip(event, 'fs12', 72)" class="fn">abs</span> <span onmouseout="hideTip(event, 'fs29', 73)" onmouseover="showTip(event, 'fs29', 73)" class="id">d</span> <span class="k">do</span>
<span class="k">let</span> <span class="id">_</span><span class="pn">,</span><span class="id">_</span><span class="pn">,</span><span class="id">_</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs31', 74)" onmouseover="showTip(event, 'fs31', 74)" class="id">i</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs10', 75)" onmouseover="showTip(event, 'fs10', 75)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs32', 76)" onmouseover="showTip(event, 'fs32', 76)" class="id">mapi2</span> <span class="pn">(</span><span class="k">fun</span> <span onmouseout="hideTip(event, 'fs31', 77)" onmouseover="showTip(event, 'fs31', 77)" class="id">i</span> <span onmouseout="hideTip(event, 'fs23', 78)" onmouseover="showTip(event, 'fs23', 78)" class="id">wi</span> <span onmouseout="hideTip(event, 'fs33', 79)" onmouseover="showTip(event, 'fs33', 79)" class="id">ni</span> <span class="k">-></span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs34', 80)" onmouseover="showTip(event, 'fs34', 80)" class="id">absErr</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs22', 81)" onmouseover="showTip(event, 'fs22', 81)" class="fn">absoluteError</span> <span onmouseout="hideTip(event, 'fs23', 82)" onmouseover="showTip(event, 'fs23', 82)" class="id">wi</span> <span onmouseout="hideTip(event, 'fs33', 83)" onmouseover="showTip(event, 'fs33', 83)" class="id">ni</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs35', 84)" onmouseover="showTip(event, 'fs35', 84)" class="fn">sign</span> <span onmouseout="hideTip(event, 'fs29', 85)" onmouseover="showTip(event, 'fs29', 85)" class="id">d</span><span class="pn">)</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs36', 86)" onmouseover="showTip(event, 'fs36', 86)" class="id">relErr</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs34', 87)" onmouseover="showTip(event, 'fs34', 87)" class="id">absErr</span> <span class="o">/</span> <span onmouseout="hideTip(event, 'fs12', 88)" onmouseover="showTip(event, 'fs12', 88)" class="fn">abs</span> <span onmouseout="hideTip(event, 'fs23', 89)" onmouseover="showTip(event, 'fs23', 89)" class="id">wi</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs37', 90)" onmouseover="showTip(event, 'fs37', 90)" class="id">weight</span> <span class="o">=</span> <span class="k">if</span> <span onmouseout="hideTip(event, 'fs6', 91)" onmouseover="showTip(event, 'fs6', 91)" class="id">wt</span> <span class="o">></span> <span class="n">0.0</span> <span class="k">then</span> <span class="o">-</span><span onmouseout="hideTip(event, 'fs23', 92)" onmouseover="showTip(event, 'fs23', 92)" class="id">wi</span> <span class="k">else</span> <span onmouseout="hideTip(event, 'fs23', 93)" onmouseover="showTip(event, 'fs23', 93)" class="id">wi</span>
<span onmouseout="hideTip(event, 'fs34', 94)" onmouseover="showTip(event, 'fs34', 94)" class="id">absErr</span><span class="pn">,</span> <span onmouseout="hideTip(event, 'fs36', 95)" onmouseover="showTip(event, 'fs36', 95)" class="id">relErr</span><span class="pn">,</span> <span onmouseout="hideTip(event, 'fs37', 96)" onmouseover="showTip(event, 'fs37', 96)" class="id">weight</span><span class="pn">,</span> <span onmouseout="hideTip(event, 'fs31', 97)" onmouseover="showTip(event, 'fs31', 97)" class="id">i</span>
<span class="pn">)</span> <span onmouseout="hideTip(event, 'fs5', 98)" onmouseover="showTip(event, 'fs5', 98)" class="id">weights</span> <span onmouseout="hideTip(event, 'fs20', 99)" onmouseover="showTip(event, 'fs20', 99)" class="id">ns</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs10', 100)" onmouseover="showTip(event, 'fs10', 100)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs38', 101)" onmouseover="showTip(event, 'fs38', 101)" class="id">min</span>
<span onmouseout="hideTip(event, 'fs20', 102)" onmouseover="showTip(event, 'fs20', 102)" class="id">ns</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs31', 103)" onmouseover="showTip(event, 'fs31', 103)" class="id">i</span><span class="pn">]</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs20', 104)" onmouseover="showTip(event, 'fs20', 104)" class="id">ns</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs31', 105)" onmouseover="showTip(event, 'fs31', 105)" class="id">i</span><span class="pn">]</span> <span class="o">+</span> <span onmouseout="hideTip(event, 'fs35', 106)" onmouseover="showTip(event, 'fs35', 106)" class="fn">sign</span> <span onmouseout="hideTip(event, 'fs29', 107)" onmouseover="showTip(event, 'fs29', 107)" class="id">d</span>
<span onmouseout="hideTip(event, 'fs39', 108)" onmouseover="showTip(event, 'fs39', 108)" class="uc">Some</span> <span onmouseout="hideTip(event, 'fs20', 109)" onmouseover="showTip(event, 'fs20', 109)" class="id">ns</span>
</code></pre>
<h2><a name="Testing" class="anchor" href="#Testing">Testing</a></h2>
<p>The <code>twitter tricky</code> test below is interesting.
It is not clear which values should be adjusted down.
Neither the largest or smallest weights look like good candidates.
The error minimisation algorithm sensibly selects the second largest weight and keeps the correct order.</p>
<p>Testing also shows the algorithm is sensitive to weights that are close together and issues with machine precision.
This directs how the error function needs to calculate.</p>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="k">let</span> <span onmouseout="hideTip(event, 'fs56', 146)" onmouseover="showTip(event, 'fs56', 146)" class="id">roundingTests</span> <span class="o">=</span>
<span class="id">testList</span> <span class="s">"rounding"</span> <span class="pn">[</span>
<span class="id">test</span> <span class="s">"empty"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">r</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs3', 147)" onmouseover="showTip(event, 'fs3', 147)" class="id">distribute</span> <span class="n">1</span> <span class="pn">[|</span><span class="pn">|]</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="id">r</span> <span onmouseout="hideTip(event, 'fs16', 148)" onmouseover="showTip(event, 'fs16', 148)" class="id">None</span> <span class="s">"empty"</span>
<span class="pn">}</span>
<span class="id">test</span> <span class="s">"n zero"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">r</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs3', 149)" onmouseover="showTip(event, 'fs3', 149)" class="id">distribute</span> <span class="n">0</span> <span class="pn">[|</span><span class="n">406.0</span><span class="pn">;</span><span class="n">348.0</span><span class="pn">;</span><span class="n">246.0</span><span class="pn">;</span><span class="n">0.0</span><span class="pn">|]</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="id">r</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs39', 150)" onmouseover="showTip(event, 'fs39', 150)" class="id">Some</span> <span class="pn">[|</span><span class="n">0</span><span class="pn">;</span><span class="n">0</span><span class="pn">;</span><span class="n">0</span><span class="pn">;</span><span class="n">0</span><span class="pn">|]</span><span class="pn">)</span> <span class="s">"zero"</span>
<span class="pn">}</span>
<span class="id">test</span> <span class="s">"twitter"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">r</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs3', 151)" onmouseover="showTip(event, 'fs3', 151)" class="id">distribute</span> <span class="n">100</span> <span class="pn">[|</span><span class="n">406.0</span><span class="pn">;</span><span class="n">348.0</span><span class="pn">;</span><span class="n">246.0</span><span class="pn">;</span><span class="n">0.0</span><span class="pn">|]</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="id">r</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs39', 152)" onmouseover="showTip(event, 'fs39', 152)" class="id">Some</span> <span class="pn">[|</span><span class="n">40</span><span class="pn">;</span><span class="n">35</span><span class="pn">;</span><span class="n">25</span><span class="pn">;</span><span class="n">0</span><span class="pn">|]</span><span class="pn">)</span> <span class="s">"40 etc"</span>
<span class="pn">}</span>
<span class="id">test</span> <span class="s">"twitter n negative"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">r</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs3', 153)" onmouseover="showTip(event, 'fs3', 153)" class="id">distribute</span> <span class="o">-</span><span class="n">100</span> <span class="pn">[|</span><span class="n">406.0</span><span class="pn">;</span><span class="n">348.0</span><span class="pn">;</span><span class="n">246.0</span><span class="pn">;</span><span class="n">0.0</span><span class="pn">|]</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="id">r</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs39', 154)" onmouseover="showTip(event, 'fs39', 154)" class="id">Some</span> <span class="pn">[|</span><span class="o">-</span><span class="n">40</span><span class="pn">;</span><span class="o">-</span><span class="n">35</span><span class="pn">;</span><span class="o">-</span><span class="n">25</span><span class="pn">;</span><span class="n">0</span><span class="pn">|]</span><span class="pn">)</span> <span class="s">"-40 etc"</span>
<span class="pn">}</span>
<span class="id">test</span> <span class="s">"twitter weights negative"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">r</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs3', 155)" onmouseover="showTip(event, 'fs3', 155)" class="id">distribute</span> <span class="n">100</span> <span class="pn">[|</span><span class="o">-</span><span class="n">406.0</span><span class="pn">;</span><span class="o">-</span><span class="n">348.0</span><span class="pn">;</span><span class="o">-</span><span class="n">246.0</span><span class="pn">;</span><span class="o">-</span><span class="n">0.0</span><span class="pn">|]</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="id">r</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs39', 156)" onmouseover="showTip(event, 'fs39', 156)" class="id">Some</span> <span class="pn">[|</span><span class="n">40</span><span class="pn">;</span><span class="n">35</span><span class="pn">;</span><span class="n">25</span><span class="pn">;</span><span class="n">0</span><span class="pn">|]</span><span class="pn">)</span> <span class="s">"40 etc"</span>
<span class="pn">}</span>
<span class="id">test</span> <span class="s">"twitter both negative"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">r</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs3', 157)" onmouseover="showTip(event, 'fs3', 157)" class="id">distribute</span> <span class="o">-</span><span class="n">100</span> <span class="pn">[|</span><span class="o">-</span><span class="n">406.0</span><span class="pn">;</span><span class="o">-</span><span class="n">348.0</span><span class="pn">;</span><span class="o">-</span><span class="n">246.0</span><span class="pn">;</span><span class="o">-</span><span class="n">0.0</span><span class="pn">|]</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="id">r</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs39', 158)" onmouseover="showTip(event, 'fs39', 158)" class="id">Some</span> <span class="pn">[|</span><span class="o">-</span><span class="n">40</span><span class="pn">;</span><span class="o">-</span><span class="n">35</span><span class="pn">;</span><span class="o">-</span><span class="n">25</span><span class="pn">;</span><span class="n">0</span><span class="pn">|]</span><span class="pn">)</span> <span class="s">"-40 etc"</span>
<span class="pn">}</span>
<span class="id">test</span> <span class="s">"twitter tricky"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">r</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs3', 159)" onmouseover="showTip(event, 'fs3', 159)" class="id">distribute</span> <span class="n">100</span> <span class="pn">[|</span><span class="n">404.0</span><span class="pn">;</span><span class="n">397.0</span><span class="pn">;</span><span class="n">57.0</span><span class="pn">;</span><span class="n">57.0</span><span class="pn">;</span><span class="n">57.0</span><span class="pn">;</span><span class="n">28.0</span><span class="pn">|]</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="id">r</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs39', 160)" onmouseover="showTip(event, 'fs39', 160)" class="id">Some</span> <span class="pn">[|</span><span class="n">40</span><span class="pn">;</span><span class="n">39</span><span class="pn">;</span><span class="n">6</span><span class="pn">;</span><span class="n">6</span><span class="pn">;</span><span class="n">6</span><span class="pn">;</span><span class="n">3</span><span class="pn">|]</span><span class="pn">)</span> <span class="s">"o no"</span>
<span class="pn">}</span>
<span class="id">test</span> <span class="s">"negative example"</span> <span class="pn">{</span>
<span class="k">let</span> <span class="id">r1</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs3', 161)" onmouseover="showTip(event, 'fs3', 161)" class="id">distribute</span> <span class="n">42</span> <span class="pn">[|</span><span class="n">1.5</span><span class="pn">;</span><span class="n">1.0</span><span class="pn">;</span><span class="n">39.5</span><span class="pn">;</span><span class="o">-</span><span class="n">1.0</span><span class="pn">;</span><span class="n">1.0</span><span class="pn">|]</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="id">r1</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs39', 162)" onmouseover="showTip(event, 'fs39', 162)" class="id">Some</span> <span class="pn">[|</span><span class="n">2</span><span class="pn">;</span><span class="n">1</span><span class="pn">;</span><span class="n">39</span><span class="pn">;</span><span class="o">-</span><span class="n">1</span><span class="pn">;</span><span class="n">1</span><span class="pn">|]</span><span class="pn">)</span> <span class="s">"2 etc"</span>
<span class="k">let</span> <span class="id">r2</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs3', 163)" onmouseover="showTip(event, 'fs3', 163)" class="id">distribute</span> <span class="o">-</span><span class="n">42</span> <span class="pn">[|</span><span class="n">1.5</span><span class="pn">;</span><span class="n">1.0</span><span class="pn">;</span><span class="n">39.5</span><span class="pn">;</span><span class="o">-</span><span class="n">1.0</span><span class="pn">;</span><span class="n">1.0</span><span class="pn">|]</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="id">r2</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs39', 164)" onmouseover="showTip(event, 'fs39', 164)" class="id">Some</span> <span class="pn">[|</span><span class="o">-</span><span class="n">2</span><span class="pn">;</span><span class="o">-</span><span class="n">1</span><span class="pn">;</span><span class="o">-</span><span class="n">39</span><span class="pn">;</span><span class="n">1</span><span class="pn">;</span><span class="o">-</span><span class="n">1</span><span class="pn">|]</span><span class="pn">)</span> <span class="s">"-2 etc"</span>
<span class="pn">}</span>
<span onmouseout="hideTip(event, 'fs50', 165)" onmouseover="showTip(event, 'fs50', 165)" class="id">testProp</span> <span class="s">"ni total correctly"</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">n</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs48', 166)" onmouseover="showTip(event, 'fs48', 166)" class="id">Gen</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs49', 167)" onmouseover="showTip(event, 'fs49', 167)" class="id">RationalFloats</span> <span class="id">ws</span><span class="pn">)</span> <span class="k">-></span>
<span onmouseout="hideTip(event, 'fs3', 168)" onmouseover="showTip(event, 'fs3', 168)" class="id">distribute</span> <span class="id">n</span> <span class="id">ws</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs57', 169)" onmouseover="showTip(event, 'fs57', 169)" class="id">Option</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs58', 170)" onmouseover="showTip(event, 'fs58', 170)" class="id">iter</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">ns</span> <span class="k">-></span> <span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs7', 171)" onmouseover="showTip(event, 'fs7', 171)" class="id">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs8', 172)" onmouseover="showTip(event, 'fs8', 172)" class="id">sum</span> <span class="id">ns</span><span class="pn">)</span> <span class="id">n</span> <span class="s">"sum ns = n"</span><span class="pn">)</span>
<span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs50', 173)" onmouseover="showTip(event, 'fs50', 173)" class="id">testProp</span> <span class="s">"negative n returns opposite of positive n"</span> <span class="pn">(</span>
<span class="k">fun</span> <span class="id">n</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs48', 174)" onmouseover="showTip(event, 'fs48', 174)" class="id">Gen</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs49', 175)" onmouseover="showTip(event, 'fs49', 175)" class="id">RationalFloats</span> <span class="id">ws</span><span class="pn">)</span> <span class="k">-></span>
<span class="k">let</span> <span class="id">r1</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs3', 176)" onmouseover="showTip(event, 'fs3', 176)" class="id">distribute</span> <span class="o">-</span><span class="id">n</span> <span class="id">ws</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs57', 177)" onmouseover="showTip(event, 'fs57', 177)" class="id">Option</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs59', 178)" onmouseover="showTip(event, 'fs59', 178)" class="id">map</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs7', 179)" onmouseover="showTip(event, 'fs7', 179)" class="id">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs21', 180)" onmouseover="showTip(event, 'fs21', 180)" class="id">map</span> <span class="pn">(</span><span class="o">~-</span><span class="pn">)</span><span class="pn">)</span>
<span class="k">let</span> <span class="id">r2</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs3', 181)" onmouseover="showTip(event, 'fs3', 181)" class="id">distribute</span> <span class="id">n</span> <span class="id">ws</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">equal</span> <span class="id">r1</span> <span class="id">r2</span> <span class="s">"r1 = r2"</span>
<span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs50', 182)" onmouseover="showTip(event, 'fs50', 182)" class="id">testProp</span> <span class="s">"increase with weight"</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">n</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs48', 183)" onmouseover="showTip(event, 'fs48', 183)" class="id">Gen</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs49', 184)" onmouseover="showTip(event, 'fs49', 184)" class="id">RationalFloats</span> <span class="id">ws</span><span class="pn">)</span> <span class="k">-></span>
<span class="k">let</span> <span class="id">d</span> <span class="o">=</span> <span class="k">if</span> <span onmouseout="hideTip(event, 'fs10', 185)" onmouseover="showTip(event, 'fs10', 185)" class="id">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs60', 186)" onmouseover="showTip(event, 'fs60', 186)" class="id">sum</span> <span class="id">ws</span> <span class="pn">></span> <span class="n">0.0</span> <span class="o"><></span> <span class="pn">(</span><span class="id">n</span><span class="pn">></span><span class="n">0</span><span class="pn">)</span> <span class="k">then</span> <span class="o">-</span><span class="n">1</span> <span class="k">else</span> <span class="n">1</span>
<span onmouseout="hideTip(event, 'fs3', 187)" onmouseover="showTip(event, 'fs3', 187)" class="id">distribute</span> <span class="id">n</span> <span class="id">ws</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs57', 188)" onmouseover="showTip(event, 'fs57', 188)" class="id">Option</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs58', 189)" onmouseover="showTip(event, 'fs58', 189)" class="id">iter</span> <span class="pn">(</span>
<span onmouseout="hideTip(event, 'fs10', 190)" onmouseover="showTip(event, 'fs10', 190)" class="id">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs61', 191)" onmouseover="showTip(event, 'fs61', 191)" class="id">map</span> <span class="pn">(</span><span class="o">(*)</span><span class="id">d</span><span class="pn">)</span>
<span class="pn">></span><span class="pn">></span> <span onmouseout="hideTip(event, 'fs10', 192)" onmouseover="showTip(event, 'fs10', 192)" class="id">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs62', 193)" onmouseover="showTip(event, 'fs62', 193)" class="id">zip</span> <span class="id">ws</span>
<span class="pn">></span><span class="pn">></span> <span onmouseout="hideTip(event, 'fs10', 194)" onmouseover="showTip(event, 'fs10', 194)" class="id">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs63', 195)" onmouseover="showTip(event, 'fs63', 195)" class="id">sort</span>
<span class="pn">></span><span class="pn">></span> <span onmouseout="hideTip(event, 'fs10', 196)" onmouseover="showTip(event, 'fs10', 196)" class="id">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs64', 197)" onmouseover="showTip(event, 'fs64', 197)" class="id">pairwise</span>
<span class="pn">></span><span class="pn">></span> <span onmouseout="hideTip(event, 'fs10', 198)" onmouseover="showTip(event, 'fs10', 198)" class="id">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs65', 199)" onmouseover="showTip(event, 'fs65', 199)" class="id">iter</span> <span class="pn">(</span><span class="k">fun</span> <span class="pn">(</span><span class="id">ni1</span><span class="pn">,</span><span class="id">ni2</span><span class="pn">)</span> <span class="k">-></span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">isLessThanOrEqual</span> <span class="id">ni1</span> <span class="id">ni2</span> <span class="s">"ni1 <= ni2"</span><span class="pn">)</span>
<span class="pn">)</span>
<span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs50', 200)" onmouseover="showTip(event, 'fs50', 200)" class="id">testProp</span> <span class="s">"smallest error"</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">n</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs48', 201)" onmouseover="showTip(event, 'fs48', 201)" class="id">Gen</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs49', 202)" onmouseover="showTip(event, 'fs49', 202)" class="id">RationalFloats</span> <span class="id">ws</span><span class="pn">)</span> <span class="id">randomChanges</span> <span class="k">-></span>
<span onmouseout="hideTip(event, 'fs3', 203)" onmouseover="showTip(event, 'fs3', 203)" class="id">distribute</span> <span class="id">n</span> <span class="id">ws</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs57', 204)" onmouseover="showTip(event, 'fs57', 204)" class="id">Option</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs58', 205)" onmouseover="showTip(event, 'fs58', 205)" class="id">iter</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">ns</span> <span class="k">-></span>
<span class="k">let</span> <span class="id">totalError</span> <span class="id">ns</span> <span class="o">=</span>
<span class="k">let</span> <span class="id">wt</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs10', 206)" onmouseover="showTip(event, 'fs10', 206)" class="id">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs60', 207)" onmouseover="showTip(event, 'fs60', 207)" class="id">sum</span> <span class="id">ws</span>
<span onmouseout="hideTip(event, 'fs10', 208)" onmouseover="showTip(event, 'fs10', 208)" class="id">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs66', 209)" onmouseover="showTip(event, 'fs66', 209)" class="id">map2</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">ni</span> <span class="id">wi</span> <span class="k">-></span>
<span onmouseout="hideTip(event, 'fs13', 210)" onmouseover="showTip(event, 'fs13', 210)" class="id">float</span> <span class="id">ni</span> <span class="pn">*</span> <span class="id">wt</span> <span class="o">/</span> <span onmouseout="hideTip(event, 'fs13', 211)" onmouseover="showTip(event, 'fs13', 211)" class="id">float</span> <span class="id">n</span> <span class="o">-</span> <span class="id">wi</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs12', 212)" onmouseover="showTip(event, 'fs12', 212)" class="id">abs</span>
<span class="pn">)</span> <span class="id">ns</span> <span class="id">ws</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs10', 213)" onmouseover="showTip(event, 'fs10', 213)" class="id">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs60', 214)" onmouseover="showTip(event, 'fs60', 214)" class="id">sum</span>
<span class="k">let</span> <span class="id">errorBefore</span> <span class="o">=</span> <span class="id">totalError</span> <span class="id">ns</span>
<span class="k">let</span> <span class="id">l</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs7', 215)" onmouseover="showTip(event, 'fs7', 215)" class="id">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs67', 216)" onmouseover="showTip(event, 'fs67', 216)" class="id">length</span> <span class="id">ns</span>
<span onmouseout="hideTip(event, 'fs68', 217)" onmouseover="showTip(event, 'fs68', 217)" class="id">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs69', 218)" onmouseover="showTip(event, 'fs69', 218)" class="id">iter</span> <span class="pn">(</span><span class="k">fun</span> <span class="pn">(</span><span class="id">i</span><span class="pn">,</span><span class="id">j</span><span class="pn">)</span> <span class="k">-></span>
<span class="id">ns</span><span class="pn">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs12', 219)" onmouseover="showTip(event, 'fs12', 219)" class="id">abs</span> <span class="id">i</span> <span class="o">%</span> <span class="id">l</span><span class="pn">]</span> <span class="k"><-</span> <span class="id">ns</span><span class="pn">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs12', 220)" onmouseover="showTip(event, 'fs12', 220)" class="id">abs</span> <span class="id">i</span> <span class="o">%</span> <span class="id">l</span><span class="pn">]</span> <span class="o">-</span> <span class="n">1</span>
<span class="id">ns</span><span class="pn">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs12', 221)" onmouseover="showTip(event, 'fs12', 221)" class="id">abs</span> <span class="id">j</span> <span class="o">%</span> <span class="id">l</span><span class="pn">]</span> <span class="k"><-</span> <span class="id">ns</span><span class="pn">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs12', 222)" onmouseover="showTip(event, 'fs12', 222)" class="id">abs</span> <span class="id">j</span> <span class="o">%</span> <span class="id">l</span><span class="pn">]</span> <span class="o">+</span> <span class="n">1</span>
<span class="pn">)</span> <span class="id">randomChanges</span>
<span class="k">let</span> <span class="id">errorAfter</span> <span class="o">=</span> <span class="id">totalError</span> <span class="id">ns</span>
<span class="id">Expect</span><span class="pn">.</span><span class="id">floatLessThanOrClose</span>
<span class="id">Accuracy</span><span class="pn">.</span><span class="id">veryHigh</span> <span class="id">errorBefore</span> <span class="id">errorAfter</span> <span class="s">"before <= after"</span>
<span class="pn">)</span>
<span class="pn">)</span>
<span class="pn">]</span>
</code></pre>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p>This is an example of how property based testing can actually help in algorithm design.
It gives example failing cases that can steer you to a better solution.</p>
<p>I have seen this problem in order management systems where orders for a number of shares are to be allocated across a number of portfolios.
The buy and sell orders have a number of partial fills, but ultimately everything needs to add up in a consistent and robust way.</p>
<p>Error minimisation is the best rounding algorithm I have found.
By construction it is also the one with the smallest total rounding error.</p>
<p>I don't think there is any other general purpose algorithm that can satisfy these properties as well.</p>
<div class="tip" id="fs1">module Rounding</div>
<div class="tip" id="fs2">namespace System</div>
<div class="tip" id="fs3">val distribute : n:int -> weights:float [] -> int [] option<br /><em><br /><br /> Distribute integer n over an array of weights</em></div>
<div class="tip" id="fs4">val n : int</div>
<div class="tip" id="fs5">val weights : float []</div>
<div class="tip" id="fs6">val wt : float</div>
<div class="tip" id="fs7">type Array =<br />  member Clone : unit -> obj<br />  member CopyTo : array:Array * index:int -> unit + 1 overload<br />  member GetEnumerator : unit -> IEnumerator<br />  member GetLength : dimension:int -> int<br />  member GetLongLength : dimension:int -> int64<br />  member GetLowerBound : dimension:int -> int<br />  member GetUpperBound : dimension:int -> int<br />  member GetValue : [<ParamArray>] indices:int[] -> obj + 7 overloads<br />  member Initialize : unit -> unit<br />  member IsFixedSize : bool<br />  ...</div>
<div class="tip" id="fs8">val sum : array:'T [] -> 'T (requires member ( + ) and member get_Zero)</div>
<div class="tip" id="fs9">val isEmpty : array:'T [] -> bool</div>
<div class="tip" id="fs10">module Seq<br /><br />from Microsoft.FSharp.Collections</div>
<div class="tip" id="fs11">val maxBy : projection:('T -> 'U) -> source:seq<'T> -> 'T (requires comparison)</div>
<div class="tip" id="fs12">val abs : value:'T -> 'T (requires member Abs)</div>
<div class="tip" id="fs13">Multiple items<br />val float : value:'T -> float (requires member op_Explicit)<br /><br />--------------------<br />type float = Double<br /><br />--------------------<br />type float<'Measure> = float</div>
<div class="tip" id="fs14">type Int32 =<br />  struct<br />    member CompareTo : value:obj -> int + 1 overload<br />    member Equals : obj:obj -> bool + 1 overload<br />    member GetHashCode : unit -> int<br />    member GetTypeCode : unit -> TypeCode<br />    member ToString : unit -> string + 3 overloads<br />    static val MaxValue : int<br />    static val MinValue : int<br />    static member Parse : s:string -> int + 3 overloads<br />    static member TryParse : s:string * result:int -> bool + 1 overload<br />  end</div>
<div class="tip" id="fs15">field int.MaxValue: int = 2147483647</div>
<div class="tip" id="fs16">union case Option.None: Option<'T></div>
<div class="tip" id="fs17">val round : (float -> int)</div>
<div class="tip" id="fs18">val f : float</div>
<div class="tip" id="fs19">Multiple items<br />val int : value:'T -> int (requires member op_Explicit)<br /><br />--------------------<br />type int = int32<br /><br />--------------------<br />type int<'Measure> = int</div>
<div class="tip" id="fs20">val ns : int []</div>
<div class="tip" id="fs21">val map : mapping:('T -> 'U) -> array:'T [] -> 'U []</div>
<div class="tip" id="fs22">val absoluteError : (float -> 'a -> 'b -> float) (requires member op_Explicit and member op_Explicit)</div>
<div class="tip" id="fs23">val wi : float</div>
<div class="tip" id="fs24">val ni : 'a (requires member op_Explicit)</div>
<div class="tip" id="fs25">val d : 'b (requires member op_Explicit)</div>
<div class="tip" id="fs26">val wc : float</div>
<div class="tip" id="fs27">val wni : float</div>
<div class="tip" id="fs28">val min : e1:'T -> e2:'T -> 'T (requires comparison)</div>
<div class="tip" id="fs29">val d : int</div>
<div class="tip" id="fs30">val __ : int</div>
<div class="tip" id="fs31">val i : int</div>
<div class="tip" id="fs32">val mapi2 : mapping:(int -> 'T1 -> 'T2 -> 'U) -> source1:seq<'T1> -> source2:seq<'T2> -> seq<'U></div>
<div class="tip" id="fs33">val ni : int</div>
<div class="tip" id="fs34">val absErr : float</div>
<div class="tip" id="fs35">val sign : value:'T -> int (requires member get_Sign)</div>
<div class="tip" id="fs36">val relErr : float</div>
<div class="tip" id="fs37">val weight : float</div>
<div class="tip" id="fs38">val min : source:seq<'T> -> 'T (requires comparison)</div>
<div class="tip" id="fs39">union case Option.Some: Value: 'T -> Option<'T></div>
<div class="tip" id="fs40">Multiple items<br />union case RationalFloats.RationalFloats: float [] -> RationalFloats<br /><br />--------------------<br />type RationalFloats = | RationalFloats of float []</div>
<div class="tip" id="fs41">val rationalFloats : obj</div>
<div class="tip" id="fs42">val fraction : (int -> int -> int -> float)</div>
<div class="tip" id="fs43">val a : int</div>
<div class="tip" id="fs44">val b : int</div>
<div class="tip" id="fs45">val c : int</div>
<div class="tip" id="fs46">val private config : obj</div>
<div class="tip" id="fs47">val typeof<'T> : Type</div>
<div class="tip" id="fs48">module Gen<br /><br />from Rounding</div>
<div class="tip" id="fs49">Multiple items<br />union case Gen.RationalFloats.RationalFloats: float [] -> Gen.RationalFloats<br /><br />--------------------<br />type RationalFloats = | RationalFloats of float []</div>
<div class="tip" id="fs50">val testProp : name:'a -> 'b</div>
<div class="tip" id="fs51">val name : 'a</div>
<div class="tip" id="fs52">val ptestProp : name:'a -> 'b</div>
<div class="tip" id="fs53">val ftestProp : stdgen:'a -> name:'b -> 'c</div>
<div class="tip" id="fs54">val stdgen : 'a</div>
<div class="tip" id="fs55">val name : 'b</div>
<div class="tip" id="fs56">val roundingTests : obj</div>
<div class="tip" id="fs57">module Option<br /><br />from Microsoft.FSharp.Core</div>
<div class="tip" id="fs58">val iter : action:('T -> unit) -> option:'T option -> unit</div>
<div class="tip" id="fs59">val map : mapping:('T -> 'U) -> option:'T option -> 'U option</div>
<div class="tip" id="fs60">val sum : source:seq<'T> -> 'T (requires member ( + ) and member get_Zero)</div>
<div class="tip" id="fs61">val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U></div>
<div class="tip" id="fs62">val zip : source1:seq<'T1> -> source2:seq<'T2> -> seq<'T1 * 'T2></div>
<div class="tip" id="fs63">val sort : source:seq<'T> -> seq<'T> (requires comparison)</div>
<div class="tip" id="fs64">val pairwise : source:seq<'T> -> seq<'T * 'T></div>
<div class="tip" id="fs65">val iter : action:('T -> unit) -> source:seq<'T> -> unit</div>
<div class="tip" id="fs66">val map2 : mapping:('T1 -> 'T2 -> 'U) -> source1:seq<'T1> -> source2:seq<'T2> -> seq<'U></div>
<div class="tip" id="fs67">val length : array:'T [] -> int</div>
<div class="tip" id="fs68">Multiple items<br />module List<br /><br />from Microsoft.FSharp.Collections<br /><br />--------------------<br />type List<'T> =<br />  | ( [] )<br />  | ( :: ) of Head: 'T * Tail: 'T list<br />    interface IReadOnlyList<'T><br />    interface IReadOnlyCollection<'T><br />    interface IEnumerable<br />    interface IEnumerable<'T><br />    member GetSlice : startIndex:int option * endIndex:int option -> 'T list<br />    member Head : 'T<br />    member IsEmpty : bool<br />    member Item : index:int -> 'T with get<br />    member Length : int<br />    member Tail : 'T list<br />    ...</div>
<div class="tip" id="fs69">val iter : action:('T -> unit) -> list:'T list -> unit</div>
Data-First Architecture
2018-02-01T00:00:00+00:00
http://anthonylloyd.github.io/blog/2018/02/01/architecture-data-first
<p>I recently had a light bulb moment when I saw a <a href="https://twitter.com/etodd_/status/936587511580844032">tweet</a> from Evan Todd.
It helped bring together some ideas I have had for a while on software architecture.</p>
<blockquote>
<p>Data characteristics excluding software functionality should dictate the system architecture.</p>
</blockquote>
<p>The shape, size and rate of change of the data are the most important factors when starting to architect a system.
The first thing to do is estimate these characteristics in average and extreme cases.</p>
<p>Functional programming encourages this mindset since the data and functions are kept separate.
F# has particular strengths in data-oriented programming.</p>
<p>I am going to make the case with an example.
I will argue most asset management systems store and use the wrong data.
This limits functionality and increases system complexity.</p>
<img style="border:1px solid black" src="/public/twitter/10_servers.png" title="10 Servers"/>
<h2><a name="Traditional-Approach" class="anchor" href="#Traditional-Approach">Traditional Approach</a></h2>
<p>Most asset management systems consider <code>positions</code>, <code>profit</code> and <code>returns</code> to be their primary data.
You can see this as they normally have overnight batch processes that generate and save <code>positions</code> for the next day.</p>
<p>This produces an enormous amount of duplicate data.
Databases are large and grow rapidly.
What is being saved is essentially a chosen set of calculation results.</p>
<p>Worse is that other processes are built on top of this position data such as adjustments, lock down and fund aggregation.</p>
<p>This architecture comes from not investigating the characteristics of the data first and jumping straight to thinking about system entities and functionality.</p>
<img style="border:1px solid black;padding: 10px 20px" src="/public/twitter/gs.png" title="GS"/>
<h2><a name="Data-First-Approach" class="anchor" href="#Data-First-Approach">Data-First Approach</a></h2>
<p>The primary data for asset management is asset <code>terms</code>, price <code>timeseries</code> and <code>trades</code>.
All other position data are just calculations based on these.
We can ignore these for now and consider caching of calculations at a later stage.</p>
<ul>
<li><code>terms</code> data is complex in structure but relatively small and changes infrequently. Event sourcing works well here for audit and a changing schema.</li>
<li><code>timeseries</code> data is simple in structure and can be efficiently compressed down to 10-20% of its original size.</li>
<li><code>trades</code> data is a simple list of asset quantity flows from one entity to another. The data is effectively all numeric and fixed size. A ledger style append only structure works well here.</li>
</ul>
<p>We can use the <a href="https://www.ishares.com/uk/intermediaries/en/products/etf-product-list#!type=emeaIshares&tab=overview&view=list">iShares</a> fund range as an extreme example.
They have many funds and trade far more often than most asset managers.</p>
<p>Downloading these funds over a period and focusing on the trade data gives us some useful statistics:</p>
<ul>
<li>Total of 280 funds.</li>
<li>Ranging from 50 to 5000 positions per fund.</li>
<li>An average of 57 trades per day per fund.</li>
<li>The average trade values can be stored in less than 128 bytes.</li>
<li>A fund for 1 year would be around 1.7 MB.</li>
<li>A fund for 10 years would be around 17 MB.</li>
<li>280 funds for 10 years would be around 5 GB.</li>
</ul>
<p>Now we have a good feel for the data we can start to make some decisions about the architecture.</p>
<p>Given the sizes we can decide to load and cache by whole fund history.
This will simplify the code, especially in the data access layer, and give a greater number of profit and return measures that can be offered.
Most of these calculations are ideally performed as a single pass through the ordered trades stored in a sensible structure.
It turns out with in memory data this requires negligible processing time and can just be done as the screen refreshes.</p>
<p>More advanced functionality can be offered, such as looking at a hierarchy of funds and perform calculations at a parent level, with various degrees of filtering and aggregation.
As the data is bitemporal we can easily ask questions such as "what did this report look like previously?" or even "what was responsible for a change in a calculation result?".
Since the data is append only we can just update for latest additions and save cloud costs.</p>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<img style="border:1px solid black" src="/public/twitter/fig_data.png" title="Fig data"/>
<p>By first understanding the data, we can build a system that is simpler, faster, more flexible and cheaper to host.</p>
<p>Software developers cannot always answer questions on the size and characteristics of their system's data. It has been abstracted away from them.
People are often surprised that full fund history can be held in memory and queried.</p>
<p>We are not google. Our extreme cases will be easier to estimate.
Infinitely scalable by default leads to complexity and poor performance.</p>
<p>With cloud computing, where architectural costs are obvious, right sizing is essential.</p>
<p>Most of the references I could find come from the games industry.
I would be interested to hear about any other examples or counterexamples.</p>
<h2><a name="References" class="anchor" href="#References">References</a></h2>
<p><a href="http://etodd.io/2015/09/28/one-weird-trick-better-code/">The One Weird Trick: data first, not code first - Even Todd</a><br />
<a href="https://news.ycombinator.com/item?id=10291688">Data first, not code first - Hacker News</a><br />
<a href="http://gamedevs.org/uploads/practical-examples-in-data-oriented-design.pdf">Practical Examples in Data Oriented Design - Niklas Frykholm</a><br />
<a href="http://gamesfromwithin.com/data-oriented-design">Data-Oriented Design - Noel Llopis</a><br />
<a href="https://martinfowler.com/articles/lmax.html#QueuesAndTheirLackOfMechanicalSympathy">Queues and their lack of mechanical sympathy - Martin Fowler</a></p>
<h2><a name="FAQ-some-questions-I-ve-been-asked" class="anchor" href="#FAQ-some-questions-I-ve-been-asked">FAQ - some questions I've been asked</a></h2>
<ol>
<li>
<p>How do you deal with previously reported values and make sure they will be the same in the future?</p>
<p>The data model is bitemporal so we can request any reporting data as at any prior time.
Lockdown process design becomes simply storing a timestamp for a reporting period.
Reporting can make use of lockdown timestamps to produce a complete view of prior period adjustments with full details.
Without a bitemporal data model this often becomes a reconciliation process, leading to further manual steps.</p>
</li>
<li>
<p>What about reported values changing due to code changes?</p>
<p>Reporting data can be saved when key reports are generated and used in regression testing.
Regression testing of all reports using the old and new code can also be automated.
This is very good practice for high quality systems and is not very difficult to implement.</p>
</li>
</ol>
.Net Core 2.0 Performance Notes Revisited
2017-12-30T00:00:00+00:00
http://anthonylloyd.github.io/blog/2017/12/30/dotnetcore-performance-part2
<p>This post is part of the <a href="https://sergeytihon.com/2017/10/22/f-advent-calendar-in-english-2017/">F# Advent Calendar 2017</a> series.
Many thanks to Sergey Tihon for organizing these.</p>
<p>Over the past few weeks I've been submitting improvements to some of the F# programs in the <a href="http://benchmarksgame.alioth.debian.org/">Benchmarks Game</a>.
In a previous <a href="/blog/2017/08/15/dotnetcore-performance">post</a> I did this for the C# programs.</p>
<p>Since that post things have moved on and C# is currently faster than Java for 8 out of 10 of the programs.
Java is faster for <code>regex-redux</code> as .Net Core doesn't yet have a compiled regex implementation.
For <code>k-nucleotide</code> Java makes use of a dictionary well suited to the program not available to C#.</p>
<p>Most of the submissions to the F# programs were ports of the C# code that had recently been optimised.
For <code>fasta</code> and <code>k-nucleotide</code> further optimisations were discovered.
<code>ArrayPool</code> is very useful in the case of <code>fasta</code>.
For <code>k-nucleotide</code> the largest dictionary can be constructed more efficiently in four parallel parts.</p>
<p>Another tempting optimisation was there being a one to one replacement in F# to use native pointers for arrays e.g. <code>Array.get a i</code> becomes <code>NativePtr.get a i</code>.
This only actually provided a small improvement in most cases and wasn't always done.</p>
<p>I feel I must plug Expecto's <a href="https://github.com/haf/expecto#performance-module">Expect.isFasterThan</a>.
It's a quick way of checking that one implementation is truly faster than another and has proven invaluable.</p>
<p><img src="/public/perf/half-is-faster.png" alt="isFasterThan" /></p>
<h2><a name="Results" class="anchor" href="#Results">Results</a></h2>
<p><a href="http://benchmarksgame.alioth.debian.org/u64q/csharp.html">C# vs Java</a>, <a href="http://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=fsharpcore&lang2=csharpcore">F# vs C#</a>, <a href="http://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=fsharpcore&lang2=java">F# vs Java</a>, <a href="http://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=fsharpcore&lang2=ghc">F# vs Haskell</a>, <a href="http://benchmarksgame.alioth.debian.org/u64q/fsharp.html">F# vs OCaml</a>, <a href="http://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=fsharpcore&lang2=python3">F# vs Python</a></p>
<table>
<thead>
<tr class="header">
<th align="left"><p>Program</p></th>
<th align="right"><p>C#</p></th>
<th align="right"><p>F#</p></th>
<th align="right"><p>Java</p></th>
<th align="right"><p>Haskell</p></th>
<th align="right"><p>OCaml</p></th>
<th align="right"><p>Python</p></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="left"><p>pidigits</p></td>
<td align="right"><p><strong>3.03</strong></p></td>
<td align="right"><p>3.05</p></td>
<td align="right"><p>3.12</p></td>
<td align="right"><p>Error</p></td>
<td align="right"><p>Error</p></td>
<td align="right"><p>3.43</p></td>
</tr>
<tr class="even">
<td align="left"><p>reverse-complement</p></td>
<td align="right"><p><strong>0.78</strong></p></td>
<td align="right"><p>0.82</p></td>
<td align="right"><p>1.03</p></td>
<td align="right"><p>1.40</p></td>
<td align="right"><p>0.79</p></td>
<td align="right"><p>3.26</p></td>
</tr>
<tr class="odd">
<td align="left"><p>fannkuch-redux</p></td>
<td align="right"><p><strong>14.44</strong></p></td>
<td align="right"><p>16.65</p></td>
<td align="right"><p>17.26</p></td>
<td align="right"><p>15.40</p></td>
<td align="right"><p>16.12</p></td>
<td align="right"><p>565.97</p></td>
</tr>
<tr class="even">
<td align="left"><p>binary-trees</p></td>
<td align="right"><p><strong>8.26</strong></p></td>
<td align="right"><p>8.54</p></td>
<td align="right"><p>8.34</p></td>
<td align="right"><p>23.66</p></td>
<td align="right"><p>10.03</p></td>
<td align="right"><p>93.55</p></td>
</tr>
<tr class="odd">
<td align="left"><p>n-body</p></td>
<td align="right"><p><strong>21.37</strong></p></td>
<td align="right"><p>22.86</p></td>
<td align="right"><p>22.10</p></td>
<td align="right"><p>21.43</p></td>
<td align="right"><p>21.67</p></td>
<td align="right"><p>838.39</p></td>
</tr>
<tr class="even">
<td align="left"><p>mandelbrot</p></td>
<td align="right"><p><strong>5.83</strong></p></td>
<td align="right"><p>6.66</p></td>
<td align="right"><p>6.04</p></td>
<td align="right"><p>11.69</p></td>
<td align="right"><p>55.18</p></td>
<td align="right"><p>225.24</p></td>
</tr>
<tr class="odd">
<td align="left"><p>fasta</p></td>
<td align="right"><p>2.09</p></td>
<td align="right"><p><strong>1.67</strong></p></td>
<td align="right"><p>2.33</p></td>
<td align="right"><p>9.36</p></td>
<td align="right"><p>6.00</p></td>
<td align="right"><p>59.47</p></td>
</tr>
<tr class="even">
<td align="left"><p>k-nucleotide</p></td>
<td align="right"><p>11.47</p></td>
<td align="right"><p>10.43</p></td>
<td align="right"><p><strong>8.70</strong></p></td>
<td align="right"><p>35.01</p></td>
<td align="right"><p>21.63</p></td>
<td align="right"><p>77.65</p></td>
</tr>
<tr class="odd">
<td align="left"><p>regex-redux</p></td>
<td align="right"><p>30.74</p></td>
<td align="right"><p>31.02</p></td>
<td align="right"><p><strong>10.34</strong></p></td>
<td align="right"><p>Error</p></td>
<td align="right"><p>24.66</p></td>
<td align="right"><p>15.22</p></td>
</tr>
<tr class="even">
<td align="left"><p>spectral-norm</p></td>
<td align="right"><p>4.07</p></td>
<td align="right"><p>4.22</p></td>
<td align="right"><p>4.23</p></td>
<td align="right"><p><strong>4.04</strong></p></td>
<td align="right"><p>4.31</p></td>
<td align="right"><p>180.97</p></td>
</tr>
</tbody>
</table>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p>As mentioned in the previous <a href="/blog/2017/08/15/dotnetcore-performance">post</a> there are some caveats to these results.
They represent the current state of a set of programs on a specific test machine.
However, there is enough evidence for some general conclusions.</p>
<p>The overall results for .Net Core 2.0 are very impressive compared to other managed platforms.</p>
<p>F# performance in the worst case is only 15% behind C#. F# is a high-level language that results in simpler and shorter code.
It's good that even in the extreme of a low-level performance benchmark it is not too far behind C#.</p>
<p>F# in fact shows very good performance against Java resulting in a five all draw.
This means F# would be expected to perform better than Scala or Kotlin if they were to participate in the benchmarks.</p>
<p>F# looks to have the best performance among the functional languages.
This is due to the performance of .Net Core 2.0 and being able to write F# in a functional-first style.</p>
<p>Hopefully 2018 will see continued adoption of both .Net Core 2.0 and F#.</p>
<p>Happy New Year!</p>
.Net Core 2.0 vs Java Performance Notes
2017-08-15T00:00:00+01:00
http://anthonylloyd.github.io/blog/2017/08/15/dotnetcore-performance
<p>Over the past few weeks I've been submitting improvements to some of the C# programs in the <a href="http://benchmarksgame.alioth.debian.org/">Benchmarks Game</a>.</p>
<p>When I first saw the <a href="http://benchmarksgame.alioth.debian.org/u64q/csharp.html">C# .Net Core vs Java</a> benchmarks the score was .Net Core 2.0 <strong>4</strong> - Java <strong>6</strong>.
This surprised me as I was under the impression .Net Core 2.0 performance would be very good.</p>
<p>I have concentrated on the 6 programs where Java was the fastest and not looked at the other 4 (binary-trees, spectral-norm, fasta, pidigits).</p>
<p>Below are some notes on each of these programs and some conclusions.</p>
<p>A follow up <a href="/blog/2017/12/30/dotnetcore-performance-part2">post</a> gives the latest results and covers F# also.</p>
<h3><a name="reverse-complement" class="anchor" href="#reverse-complement">reverse-complement</a></h3>
<p>Old Result: C# 1.39s - Java 1.10s</p>
<p>Changes made: Reversing in place and more efficient parallel processing.</p>
<p>New Result: C# 0.79s - Java 1.10s</p>
<h3><a name="mandelbrot" class="anchor" href="#mandelbrot">mandelbrot</a></h3>
<p>Old Result: C# 7.29s - Java 7.10s</p>
<p>Changes made: Simplified the parallel processing by using TPL Parallel.For.</p>
<p>New Result: C# 6.78s - Java 7.10s</p>
<h3><a name="n-body" class="anchor" href="#n-body">n-body</a></h3>
<p>Old Result: C# 21.71s - Java 21.54s</p>
<p>I made many failed attempts to try to improve this result e.g. low level parallel, SIMD.</p>
<p>Dissapointing as it looks like Java has a small edge on these single thread numeric calculations.</p>
<p>Some I submitted can be found <a href="https://alioth.debian.org/tracker/index.php?group_id=100815&atid=413122">here</a>.</p>
<h3><a name="fannkuch-redux" class="anchor" href="#fannkuch-redux">fannkuch-redux</a></h3>
<p>Old Result: C# 18.80s - Java 13.74s</p>
<p>I think Java has some advantage in int array manipulation.</p>
<p>I tried splitting up into more parallel blocks but the overhead outweighs the better CPU use.</p>
<p>Changes made: Small array optimisations and more efficient parallel processing.</p>
<p>New Result: C# 14.45s - Java 13.74s</p>
<h3><a name="k-nucleotide" class="anchor" href="#k-nucleotide">k-nucleotide</a></h3>
<p>Old Result: C# 13.76s - Java 07.93s</p>
<p>Java code cheats in that they have managed to find a very specific dictionary from an obscure library.</p>
<p>The dictionary count can be done in parallel but I think the Java dictionary wins it here.</p>
<p>Changes made: More efficient byte array memory use and parallel processing.</p>
<p>New Result: C# 12.37s - Java 07.93s</p>
<h3><a name="regex-redux" class="anchor" href="#regex-redux">regex-redux</a></h3>
<p>Old Result: C# 32.02s - Java 12.31s</p>
<p>Regex is not great in .Net. I don't think it's even compiled on .Net Core.</p>
<p>Changes made: Reordered the tasks to run longest to shortest.</p>
<p>New Result: C# 31.19s - Java 12.31s</p>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p>First some caveats.</p>
<p>The test machine is a single 64-bit quad core machine. This may not represent current servers.
I found optimising for performance on a different machine to be very interesting.
I tended to over optimise algorithm constants and need to look for more universal performance optimisations.</p>
<p>The performance benchmark testing process seems to be robust. There could be some bias in the configurations but I was not aware of any.</p>
<p>No toy benchmarks truly represent the performance of a large application. These benchmarks do look to solve larger real world problems than most I have seen.</p>
<p>I found the <a href="http://benchmarksgame.alioth.debian.org/">Benchmarks Game</a> to be a very good set of benchmarks.
The benchmarks are well thought through and cover classic single thread calculations through to multi-threaded IO data processing.
Obviously, areas such as network performance are not as easy to test in this kind of benchmark game.
The organisers are tough but fair and the rules make a lot of sense.
The site is efficiently run and overnight was updated to .Net Core 2.0 RTM.</p>
<p>So now the score is .Net Core 2.0 <strong>6</strong> - Java <strong>4</strong>.</p>
<p>My overall impression is that .Net Core 2.0 and Java perform about the same.
Java possibly has a small edge on some calculations and array manipulation and .Net having better parallel libraries.</p>
<p>So now I am out of ideas on how to improve these further. If you have any questions or ideas, feel free to get in touch.</p>
Kicking the Debugger habit
2017-04-30T00:00:00+01:00
http://anthonylloyd.github.io/blog/2017/04/30/kicking-the-debugger
<p>This post covers my experience of giving up breakpoint and step-through debugging.</p>
<h3><a name="My-Domain" class="anchor" href="#My-Domain">My Domain</a></h3>
<p>There are some interesting features of the domain I work in that have led me here.
Financial analytics tend to compose many algorithms such as curve fitting, statistics, monte carlo and optimisation.
The public API on the other hand is very simple: given this portfolio, return the risks associated.</p>
<p>A bug report is more likely to be 'this risk number looks a little odd' rather than 'an exception was thrown and here is the stack trace'.
I can't agree with the idea that you should only unit test your public APIs.
Rather, this should be: you need to unit test your public APIs, but if your domain is sufficiently complex, you also need to unit test internal modules.
What is key is if a bug report queries an API result how quickly could you investigate and resolve any potential issue?</p>
<h3><a name="What-s-wrong-with-debugging" class="anchor" href="#What-s-wrong-with-debugging">What's wrong with debugging?</a></h3>
<p>In my field:</p>
<ul>
<li>It's not scalable - for larger code paths or multiple threads setting breakpoints and stepping through is just not feasible. It's like finding a needle in a haystack.</li>
<li>It's limited in power - even mature debugging frameworks such as in Visual Studio are limited in the kind of conditional logic you can use while debugging.</li>
<li>It's time consuming - many a good hour can be spent pressing F5/F10/F11 in a zombie like state only to restart and try again.</li>
</ul>
<img style="border:1px solid black" src="/public/twitter/debugging.png" title="debugging"/>
<h3><a name="What-s-the-alternative" class="anchor" href="#What-s-the-alternative">What's the alternative?</a></h3>
<p>Since starting to use <a href="https://github.com/haf/expecto">Expecto</a>, I've been using the command line to run unit tests.
<a href="https://github.com/haf/expecto">Expecto</a> does integrate with Visual Studio and can even do live unit testing in VS Code.
I've found using a set of commands I've built up in FAKE to be more flexible and productive e.g.</p>
<pre class="fssnip highlighted"><code lang="fsharp"> <span class="id">test</span> <span class="id">integration</span>
<span class="id">test</span> <span class="id">all</span>
<span class="id">test</span> <span class="n">64</span> <span class="id">debug</span> <span class="o">--</span><span class="id">stress</span> <span class="n">2</span>
</code></pre>
<p><a href="https://github.com/haf/expecto">Expecto</a> encourages using normal code for organisation, setup & teardown and parameterisation of tests, instead of a limited framework of attributes.</p>
<p>Now apply this concept to debugging.
With a debug module in the core of a codebase that is conditional on the debug configuration, code can be annotated with validation and some debug output.
The command line records a history of the test results and validation output.
Once complete, compiling in release ensures all diagnostic code is removed.</p>
<p>This started out as simple functions to <code>printfn</code> data being sequenced and piped, but expanded into functions to count calls, check for NaNs globally, serialize function inputs and outputs, test convergence of numbers etc.
This is normal code and there is huge scope for adding conditional logic.</p>
<h3><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h3>
<p>Kicking the debugger habit has given me a productivity boost.
It forces me to think more logically about how I validate and break down a problem.</p>
<p>It also reduces the complexity of the tooling.
Finding and fixing bugs feels more like coding and unit testing.
I can use a simpler code editor plus the command line.</p>
<p>The result is I now have more confidence that once I've created the initial bug test I will be able to resolve it quickly.</p>
<h3><a name="Appendix" class="anchor" href="#Appendix">Appendix</a></h3>
<p>I've been asked for some sample code from the debug module.
The code below should hopefully start to give an idea of what can be done.</p>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="c">//#if DEBUG</span>
<span class="pn">[<</span><span onmouseout="hideTip(event, 'fs3', 4)" onmouseover="showTip(event, 'fs3', 4)" class="id">AutoOpen</span><span class="pn">>]</span>
<span class="k">module</span> <span class="id">OverflowAndNaNCheck</span> <span class="o">=</span>
<span class="k">open</span> <span onmouseout="hideTip(event, 'fs4', 5)" onmouseover="showTip(event, 'fs4', 5)" class="id">Checked</span>
<span class="k">let</span> <span class="k">inline</span> <span class="k">private</span> <span class="id">isNaN</span> <span class="id">v</span> <span class="o">=</span>
<span class="k">match</span> <span onmouseout="hideTip(event, 'fs5', 6)" onmouseover="showTip(event, 'fs5', 6)" class="id">box</span> <span class="id">v</span> <span class="k">with</span> <span class="pn">|</span> <span class="o">:?</span> <span onmouseout="hideTip(event, 'fs6', 7)" onmouseover="showTip(event, 'fs6', 7)" class="id">float</span> <span class="k">as</span> <span class="id">v</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs7', 8)" onmouseover="showTip(event, 'fs7', 8)" class="id">Double</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs8', 9)" onmouseover="showTip(event, 'fs8', 9)" class="id">IsNaN</span> <span class="id">v</span> <span class="pn">|</span> <span class="id">_</span> <span class="k">-></span> <span class="k">false</span>
<span class="k">let</span> <span class="k">inline</span> <span class="pn">(</span><span class="o">/</span><span class="pn">)</span> <span class="id">a</span> <span class="id">b</span> <span class="o">=</span>
<span class="k">let</span> <span class="id">c</span> <span class="o">=</span> <span class="id">a</span><span class="o">/</span><span class="id">b</span> <span class="k">in</span> <span class="k">if</span> <span class="id">isNaN</span> <span class="id">c</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs9', 10)" onmouseover="showTip(event, 'fs9', 10)" class="id">failwithf</span> <span class="s">"NaN found: %A / %A = %A"</span> <span class="id">a</span> <span class="id">b</span> <span class="id">c</span> <span class="k">else</span> <span class="id">c</span>
<span class="k">let</span> <span class="k">inline</span> <span class="pn">(</span><span class="o">+</span><span class="pn">)</span> <span class="id">a</span> <span class="id">b</span> <span class="o">=</span>
<span class="k">let</span> <span class="id">c</span> <span class="o">=</span> <span class="id">a</span><span class="o">+</span><span class="id">b</span> <span class="k">in</span> <span class="k">if</span> <span class="id">isNaN</span> <span class="id">c</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs9', 11)" onmouseover="showTip(event, 'fs9', 11)" class="id">failwithf</span> <span class="s">"NaN found: %A + %A = %A"</span> <span class="id">a</span> <span class="id">b</span> <span class="id">c</span> <span class="k">else</span> <span class="id">c</span>
<span class="k">let</span> <span class="k">inline</span> <span class="pn">(</span><span class="o">-</span><span class="pn">)</span> <span class="id">a</span> <span class="id">b</span> <span class="o">=</span>
<span class="k">let</span> <span class="id">c</span> <span class="o">=</span> <span class="id">a</span><span class="o">-</span><span class="id">b</span> <span class="k">in</span> <span class="k">if</span> <span class="id">isNaN</span> <span class="id">c</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs9', 12)" onmouseover="showTip(event, 'fs9', 12)" class="id">failwithf</span> <span class="s">"NaN found: %A - %A = %A"</span> <span class="id">a</span> <span class="id">b</span> <span class="id">c</span> <span class="k">else</span> <span class="id">c</span>
<span class="k">let</span> <span class="k">inline</span> <span class="o">(*)</span> <span class="id">a</span> <span class="id">b</span> <span class="o">=</span>
<span class="k">let</span> <span class="id">c</span> <span class="o">=</span> <span class="id">a</span><span class="pn">*</span><span class="id">b</span> <span class="k">in</span> <span class="k">if</span> <span class="id">isNaN</span> <span class="id">c</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs9', 13)" onmouseover="showTip(event, 'fs9', 13)" class="id">failwithf</span> <span class="s">"NaN found: %A * %A = %A"</span> <span class="id">a</span> <span class="id">b</span> <span class="id">c</span> <span class="k">else</span> <span class="id">c</span>
<span class="k">let</span> <span class="k">inline</span> <span class="pn">(</span> <span class="o">**</span> <span class="pn">)</span> <span class="id">a</span> <span class="id">b</span> <span class="o">=</span>
<span class="k">let</span> <span class="id">c</span> <span class="o">=</span> <span class="id">a</span><span class="o">**</span><span class="id">b</span> <span class="k">in</span> <span class="k">if</span> <span class="id">isNaN</span> <span class="id">c</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs9', 14)" onmouseover="showTip(event, 'fs9', 14)" class="id">failwithf</span> <span class="s">"NaN found: %A ** %A = %A"</span> <span class="id">a</span> <span class="id">b</span> <span class="id">c</span> <span class="k">else</span> <span class="id">c</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs10', 15)" onmouseover="showTip(event, 'fs10', 15)" class="id">sqrt</span> <span class="id">a</span> <span class="o">=</span>
<span class="k">let</span> <span class="id">c</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs10', 16)" onmouseover="showTip(event, 'fs10', 16)" class="id">sqrt</span> <span class="id">a</span> <span class="k">in</span> <span class="k">if</span> <span class="id">isNaN</span> <span class="id">c</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs9', 17)" onmouseover="showTip(event, 'fs9', 17)" class="id">failwithf</span> <span class="s">"NaN found: sqrt %A = %A"</span> <span class="id">a</span> <span class="id">c</span> <span class="k">else</span> <span class="id">c</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs11', 18)" onmouseover="showTip(event, 'fs11', 18)" class="id">log</span> <span class="id">a</span> <span class="o">=</span>
<span class="k">let</span> <span class="id">c</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs11', 19)" onmouseover="showTip(event, 'fs11', 19)" class="id">log</span> <span class="id">a</span> <span class="k">in</span> <span class="k">if</span> <span class="id">isNaN</span> <span class="id">c</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs9', 20)" onmouseover="showTip(event, 'fs9', 20)" class="id">failwithf</span> <span class="s">"NaN found: log %A = %A"</span> <span class="id">a</span> <span class="id">c</span> <span class="k">else</span> <span class="id">c</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs12', 21)" onmouseover="showTip(event, 'fs12', 21)" class="id">log10</span> <span class="id">a</span> <span class="o">=</span>
<span class="k">let</span> <span class="id">c</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs12', 22)" onmouseover="showTip(event, 'fs12', 22)" class="id">log10</span> <span class="id">a</span> <span class="k">in</span> <span class="k">if</span> <span class="id">isNaN</span> <span class="id">c</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs9', 23)" onmouseover="showTip(event, 'fs9', 23)" class="id">failwithf</span> <span class="s">"NaN found: log10 %A = %A"</span> <span class="id">a</span> <span class="id">c</span> <span class="k">else</span> <span class="id">c</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs13', 24)" onmouseover="showTip(event, 'fs13', 24)" class="id">asin</span> <span class="id">a</span> <span class="o">=</span>
<span class="k">let</span> <span class="id">c</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs13', 25)" onmouseover="showTip(event, 'fs13', 25)" class="id">asin</span> <span class="id">a</span> <span class="k">in</span> <span class="k">if</span> <span class="id">isNaN</span> <span class="id">c</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs9', 26)" onmouseover="showTip(event, 'fs9', 26)" class="id">failwithf</span> <span class="s">"NaN found: asin %A = %A"</span> <span class="id">a</span> <span class="id">c</span> <span class="k">else</span> <span class="id">c</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs14', 27)" onmouseover="showTip(event, 'fs14', 27)" class="id">acos</span> <span class="id">a</span> <span class="o">=</span>
<span class="k">let</span> <span class="id">c</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs14', 28)" onmouseover="showTip(event, 'fs14', 28)" class="id">acos</span> <span class="id">a</span> <span class="k">in</span> <span class="k">if</span> <span class="id">isNaN</span> <span class="id">c</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs9', 29)" onmouseover="showTip(event, 'fs9', 29)" class="id">failwithf</span> <span class="s">"NaN found: acos %A = %A"</span> <span class="id">a</span> <span class="id">c</span> <span class="k">else</span> <span class="id">c</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs15', 30)" onmouseover="showTip(event, 'fs15', 30)" class="id">atan</span> <span class="id">a</span> <span class="o">=</span>
<span class="k">let</span> <span class="id">c</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs15', 31)" onmouseover="showTip(event, 'fs15', 31)" class="id">atan</span> <span class="id">a</span> <span class="k">in</span> <span class="k">if</span> <span class="id">isNaN</span> <span class="id">c</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs9', 32)" onmouseover="showTip(event, 'fs9', 32)" class="id">failwithf</span> <span class="s">"NaN found: atan %A = %A"</span> <span class="id">a</span> <span class="id">c</span> <span class="k">else</span> <span class="id">c</span>
<span class="k">module</span> <span class="id">Dbg</span> <span class="o">=</span>
<span class="k">let</span> <span class="k">private</span> <span class="id">rand</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs16', 33)" onmouseover="showTip(event, 'fs16', 33)" class="id">Random</span><span class="pn">(</span><span class="pn">)</span>
<span class="k">let</span> <span class="k">mutable</span> <span class="k">private</span> <span class="id">randN</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs17', 34)" onmouseover="showTip(event, 'fs17', 34)" class="id">None</span>
<span class="k">type</span> <span class="id">atRandom</span><span class="pn">(</span><span class="id">n</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs18', 35)" onmouseover="showTip(event, 'fs18', 35)" class="id">int</span><span class="pn">)</span> <span class="o">=</span>
<span class="k">do</span>
<span class="id">randN</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs19', 36)" onmouseover="showTip(event, 'fs19', 36)" class="id">Some</span> <span class="id">n</span>
<span class="k">interface</span> <span onmouseout="hideTip(event, 'fs20', 37)" onmouseover="showTip(event, 'fs20', 37)" class="id">IDisposable</span> <span class="k">with</span>
<span class="k">member</span> <span class="id">__</span><span class="pn">.</span><span class="id">Dispose</span><span class="pn">(</span><span class="pn">)</span> <span class="o">=</span> <span class="id">randN</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs17', 38)" onmouseover="showTip(event, 'fs17', 38)" class="id">None</span>
<span class="k">let</span> <span class="id">write</span> <span class="id">fmt</span> <span class="o">=</span>
<span class="k">let</span> <span class="id">sb</span> <span class="o">=</span>
<span class="k">let</span> <span class="id">n</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs21', 39)" onmouseover="showTip(event, 'fs21', 39)" class="id">DateTime</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs22', 40)" onmouseover="showTip(event, 'fs22', 40)" class="id">Now</span>
<span class="k">let</span> <span class="id">sb</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs23', 41)" onmouseover="showTip(event, 'fs23', 41)" class="id">StringBuilder</span><span class="pn">(</span><span class="s">"DEBUG "</span><span class="pn">)</span>
<span class="id">sb</span><span class="pn">.</span><span class="id">Append</span><span class="pn">(</span><span class="id">n</span><span class="pn">.</span><span class="id">ToString</span><span class="pn">(</span><span class="s">"dd MMM HH:mm:ss.fffffff"</span><span class="pn">)</span><span class="pn">)</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs24', 42)" onmouseover="showTip(event, 'fs24', 42)" class="id">ignore</span>
<span class="id">sb</span><span class="pn">.</span><span class="id">Append</span><span class="pn">(</span><span class="s">"> "</span><span class="pn">)</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs24', 43)" onmouseover="showTip(event, 'fs24', 43)" class="id">ignore</span>
<span class="id">sb</span>
<span onmouseout="hideTip(event, 'fs25', 44)" onmouseover="showTip(event, 'fs25', 44)" class="id">Printf</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs26', 45)" onmouseover="showTip(event, 'fs26', 45)" class="id">kbprintf</span> <span class="pn">(</span><span class="k">fun</span> <span class="pn">(</span><span class="pn">)</span> <span class="k">-></span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs27', 46)" onmouseover="showTip(event, 'fs27', 46)" class="id">Option</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs28', 47)" onmouseover="showTip(event, 'fs28', 47)" class="id">isNone</span> <span class="id">randN</span> <span class="o">||</span> <span onmouseout="hideTip(event, 'fs27', 48)" onmouseover="showTip(event, 'fs27', 48)" class="id">Option</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs29', 49)" onmouseover="showTip(event, 'fs29', 49)" class="id">get</span> <span class="id">randN</span> <span class="o">|></span> <span class="id">rand</span><span class="pn">.</span><span class="id">Next</span> <span class="o">=</span> <span class="n">0</span> <span class="k">then</span>
<span class="k">let</span> <span class="id">old</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs30', 50)" onmouseover="showTip(event, 'fs30', 50)" class="id">Console</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs31', 51)" onmouseover="showTip(event, 'fs31', 51)" class="id">ForegroundColor</span>
<span class="k">try</span>
<span onmouseout="hideTip(event, 'fs30', 52)" onmouseover="showTip(event, 'fs30', 52)" class="id">Console</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs31', 53)" onmouseover="showTip(event, 'fs31', 53)" class="id">ForegroundColor</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs32', 54)" onmouseover="showTip(event, 'fs32', 54)" class="id">ConsoleColor</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs33', 55)" onmouseover="showTip(event, 'fs33', 55)" class="id">Red</span>
<span class="id">sb</span><span class="pn">.</span><span class="id">ToString</span><span class="pn">(</span><span class="pn">)</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs30', 56)" onmouseover="showTip(event, 'fs30', 56)" class="id">Console</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs34', 57)" onmouseover="showTip(event, 'fs34', 57)" class="id">WriteLine</span>
<span class="k">finally</span>
<span onmouseout="hideTip(event, 'fs30', 58)" onmouseover="showTip(event, 'fs30', 58)" class="id">Console</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs31', 59)" onmouseover="showTip(event, 'fs31', 59)" class="id">ForegroundColor</span> <span class="k"><-</span> <span class="id">old</span>
<span class="pn">)</span> <span class="id">sb</span> <span class="id">fmt</span>
<span class="k">let</span> <span class="id">writeIf</span> <span class="id">condition</span> <span class="id">fmt</span> <span class="o">=</span> <span class="k">if</span> <span class="id">condition</span><span class="pn">(</span><span class="pn">)</span> <span class="k">then</span> <span class="id">write</span> <span class="id">fmt</span>
<span class="k">let</span> <span class="id">runIf</span> <span class="id">condition</span> <span class="id">fn</span> <span class="o">=</span> <span class="k">if</span> <span class="id">condition</span><span class="pn">(</span><span class="pn">)</span> <span class="k">then</span> <span class="id">fn</span><span class="pn">(</span><span class="pn">)</span>
<span class="k">let</span> <span class="id">pipe</span> <span class="id">fmt</span> <span class="o">=</span> <span class="k">fun</span> <span class="id">a</span> <span class="k">-></span> <span class="id">write</span> <span class="id">fmt</span> <span class="id">a</span><span class="pn">;</span> <span class="id">a</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs35', 60)" onmouseover="showTip(event, 'fs35', 60)" class="id">seq</span> <span class="id">desc</span> <span class="id">s</span> <span class="o">=</span>
<span class="k">let</span> <span class="id">s</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs36', 61)" onmouseover="showTip(event, 'fs36', 61)" class="id">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs37', 62)" onmouseover="showTip(event, 'fs37', 62)" class="id">cache</span> <span class="id">s</span>
<span onmouseout="hideTip(event, 'fs36', 63)" onmouseover="showTip(event, 'fs36', 63)" class="id">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs38', 64)" onmouseover="showTip(event, 'fs38', 64)" class="id">iter</span> <span class="pn">(</span><span class="id">write</span> <span class="s">"%s: %A"</span> <span class="id">desc</span><span class="pn">)</span> <span class="id">s</span>
<span class="id">s</span>
<span class="k">let</span> <span class="id">fun1</span> <span class="id">desc</span> <span class="pn">(</span><span class="id">f</span><span class="pn">:</span><span class="id">'</span><span class="id">a</span><span class="k">-></span><span class="id">'</span><span class="id">b</span><span class="pn">)</span> <span class="o">=</span>
<span class="k">fun</span> <span class="id">a</span> <span class="k">-></span>
<span class="k">let</span> <span class="id">b</span> <span class="o">=</span> <span class="id">f</span> <span class="id">a</span>
<span class="id">write</span> <span class="s">"%s - Input: %A\t\t Output: %A"</span> <span class="id">desc</span> <span class="id">a</span> <span class="id">b</span><span class="pn">;</span> <span class="id">b</span>
<span class="k">let</span> <span class="id">fun2</span> <span class="id">desc</span> <span class="pn">(</span><span class="id">f</span><span class="pn">:</span><span class="id">'</span><span class="id">a</span><span class="k">-></span><span class="id">'</span><span class="id">b</span><span class="k">-></span><span class="id">'</span><span class="id">c</span><span class="pn">)</span> <span class="o">=</span>
<span class="k">fun</span> <span class="id">a</span> <span class="id">b</span> <span class="k">-></span>
<span class="k">let</span> <span class="id">c</span> <span class="o">=</span> <span class="id">f</span> <span class="id">a</span> <span class="id">b</span>
<span class="id">write</span> <span class="s">"%s - Input: %A\t\t Output: %A"</span> <span class="id">desc</span> <span class="pn">(</span><span class="id">a</span><span class="pn">,</span><span class="id">b</span><span class="pn">)</span> <span class="id">c</span><span class="pn">;</span> <span class="id">c</span>
<span class="k">let</span> <span class="id">fun3</span> <span class="id">desc</span> <span class="pn">(</span><span class="id">f</span><span class="pn">:</span><span class="id">'</span><span class="id">a</span><span class="k">-></span><span class="id">'</span><span class="id">b</span><span class="k">-></span><span class="id">'</span><span class="id">c</span><span class="k">-></span><span class="id">'</span><span class="id">d</span><span class="pn">)</span> <span class="o">=</span>
<span class="k">fun</span> <span class="id">a</span> <span class="id">b</span> <span class="id">c</span> <span class="k">-></span>
<span class="k">let</span> <span class="id">d</span> <span class="o">=</span> <span class="id">f</span> <span class="id">a</span> <span class="id">b</span> <span class="id">c</span>
<span class="id">write</span> <span class="s">"%s - Input: %A\t\t Output: %A"</span> <span class="id">desc</span> <span class="pn">(</span><span class="id">a</span><span class="pn">,</span><span class="id">b</span><span class="pn">,</span><span class="id">c</span><span class="pn">)</span> <span class="id">d</span><span class="pn">;</span> <span class="id">d</span>
<span class="k">type</span> <span class="id">counter</span><span class="pn">(</span><span class="id">desc</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs39', 65)" onmouseover="showTip(event, 'fs39', 65)" class="id">string</span><span class="pn">)</span> <span class="o">=</span>
<span class="k">let</span> <span class="k">mutable</span> <span class="id">count</span> <span class="o">=</span> <span class="n">0</span>
<span class="k">member</span> <span class="id">__</span><span class="pn">.</span><span class="id">Count</span> <span class="o">=</span> <span class="id">count</span>
<span class="k">member</span> <span class="id">__</span><span class="pn">.</span><span class="id">Increment</span><span class="pn">(</span><span class="pn">)</span> <span class="o">=</span> <span class="id">count</span> <span class="k"><-</span> <span class="id">count</span> <span class="o">+</span> <span class="n">1</span>
<span class="k">member</span> <span class="k">inline</span> <span class="id">m</span><span class="pn">.</span><span class="id">Calls</span> <span class="id">fn</span> <span class="o">=</span> <span class="k">fun</span> <span class="id">a</span> <span class="k">-></span> <span class="id">m</span><span class="pn">.</span><span class="id">Increment</span><span class="pn">(</span><span class="pn">)</span><span class="pn">;</span> <span class="id">fn</span> <span class="id">a</span>
<span class="k">interface</span> <span onmouseout="hideTip(event, 'fs20', 66)" onmouseover="showTip(event, 'fs20', 66)" class="id">IDisposable</span> <span class="k">with</span>
<span class="k">member</span> <span class="id">__</span><span class="pn">.</span><span class="id">Dispose</span><span class="pn">(</span><span class="pn">)</span> <span class="o">=</span> <span class="id">write</span> <span class="s">"%s count = %i"</span> <span class="id">desc</span> <span class="id">count</span>
<span class="k">let</span> <span class="id">descendingChecker</span> <span class="id">desc</span> <span class="o">=</span>
<span class="k">let</span> <span class="k">mutable</span> <span class="id">last</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs40', 67)" onmouseover="showTip(event, 'fs40', 67)" class="id">infinity</span>
<span class="k">fun</span> <span class="id">x</span> <span class="k">-></span>
<span class="k">if</span> <span class="id">x</span><span class="pn">></span><span class="id">last</span> <span class="k">then</span> <span class="id">write</span> <span class="s">"%s - should be descending but %A > %A"</span> <span class="id">desc</span> <span class="id">x</span> <span class="id">last</span>
<span class="k">else</span> <span class="id">last</span><span class="k"><-</span><span class="id">x</span>
<span class="k">let</span> <span class="k">mutable</span> <span class="k">private</span> <span class="id">functionMap</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs41', 68)" onmouseover="showTip(event, 'fs41', 68)" class="id">Map</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs42', 69)" onmouseover="showTip(event, 'fs42', 69)" class="id">empty</span>
<span class="k">let</span> <span class="id">addFun</span> <span class="pn">(</span><span class="id">key</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs39', 70)" onmouseover="showTip(event, 'fs39', 70)" class="id">string</span><span class="pn">)</span> <span class="pn">(</span><span class="id">fn</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs43', 71)" onmouseover="showTip(event, 'fs43', 71)" class="id">unit</span><span class="k">-></span><span onmouseout="hideTip(event, 'fs43', 72)" onmouseover="showTip(event, 'fs43', 72)" class="id">unit</span><span class="pn">)</span> <span class="o">=</span> <span class="id">functionMap</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs41', 73)" onmouseover="showTip(event, 'fs41', 73)" class="id">Map</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs44', 74)" onmouseover="showTip(event, 'fs44', 74)" class="id">add</span> <span class="id">key</span> <span class="id">fn</span> <span class="id">functionMap</span>
<span class="k">let</span> <span class="id">runFun</span> <span class="pn">(</span><span class="id">key</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs39', 75)" onmouseover="showTip(event, 'fs39', 75)" class="id">string</span><span class="pn">)</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs41', 76)" onmouseover="showTip(event, 'fs41', 76)" class="id">Map</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs45', 77)" onmouseover="showTip(event, 'fs45', 77)" class="id">find</span> <span class="id">key</span> <span class="id">functionMap</span> <span class="pn">(</span><span class="pn">)</span>
<span class="c">//#endif</span>
</code></pre>
<div class="tip" id="fs1">namespace System</div>
<div class="tip" id="fs2">namespace System.Text</div>
<div class="tip" id="fs3">Multiple items<br />type AutoOpenAttribute =<br />  inherit Attribute<br />  new : unit -> AutoOpenAttribute<br />  new : path:string -> AutoOpenAttribute<br />  member Path : string<br /><br />--------------------<br />new : unit -> AutoOpenAttribute<br />new : path:string -> AutoOpenAttribute</div>
<div class="tip" id="fs4">Multiple items<br />module Checked<br /><br />from Microsoft.FSharp.Core.Operators<br /><br />--------------------<br />module Checked<br /><br />from Microsoft.FSharp.Core.ExtraTopLevelOperators</div>
<div class="tip" id="fs5">val box : value:'T -> obj</div>
<div class="tip" id="fs6">Multiple items<br />val float : value:'T -> float (requires member op_Explicit)<br /><br />--------------------<br />type float = Double<br /><br />--------------------<br />type float<'Measure> = float</div>
<div class="tip" id="fs7">type Double =<br />  struct<br />    member CompareTo : value:obj -> int + 1 overload<br />    member Equals : obj:obj -> bool + 1 overload<br />    member GetHashCode : unit -> int<br />    member GetTypeCode : unit -> TypeCode<br />    member ToString : unit -> string + 3 overloads<br />    static val MinValue : float<br />    static val MaxValue : float<br />    static val Epsilon : float<br />    static val NegativeInfinity : float<br />    static val PositiveInfinity : float<br />    ...<br />  end</div>
<div class="tip" id="fs8">Double.IsNaN(d: float) : bool</div>
<div class="tip" id="fs9">val failwithf : format:Printf.StringFormat<'T,'Result> -> 'T</div>
<div class="tip" id="fs10">val sqrt : value:'T -> 'U (requires member Sqrt)</div>
<div class="tip" id="fs11">val log : value:'T -> 'T (requires member Log)</div>
<div class="tip" id="fs12">val log10 : value:'T -> 'T (requires member Log10)</div>
<div class="tip" id="fs13">val asin : value:'T -> 'T (requires member Asin)</div>
<div class="tip" id="fs14">val acos : value:'T -> 'T (requires member Acos)</div>
<div class="tip" id="fs15">val atan : value:'T -> 'T (requires member Atan)</div>
<div class="tip" id="fs16">Multiple items<br />type Random =<br />  new : unit -> Random + 1 overload<br />  member Next : unit -> int + 2 overloads<br />  member NextBytes : buffer:byte[] -> unit<br />  member NextDouble : unit -> float<br /><br />--------------------<br />Random() : Random<br />Random(Seed: int) : Random</div>
<div class="tip" id="fs17">union case Option.None: Option<'T></div>
<div class="tip" id="fs18">Multiple items<br />val int : value:'T -> int (requires member op_Explicit)<br /><br />--------------------<br />type int = int32<br /><br />--------------------<br />type int<'Measure> = int</div>
<div class="tip" id="fs19">union case Option.Some: Value: 'T -> Option<'T></div>
<div class="tip" id="fs20">type IDisposable =<br />  member Dispose : unit -> unit</div>
<div class="tip" id="fs21">Multiple items<br />type DateTime =<br />  struct<br />    new : ticks:int64 -> DateTime + 10 overloads<br />    member Add : value:TimeSpan -> DateTime<br />    member AddDays : value:float -> DateTime<br />    member AddHours : value:float -> DateTime<br />    member AddMilliseconds : value:float -> DateTime<br />    member AddMinutes : value:float -> DateTime<br />    member AddMonths : months:int -> DateTime<br />    member AddSeconds : value:float -> DateTime<br />    member AddTicks : value:int64 -> DateTime<br />    member AddYears : value:int -> DateTime<br />    ...<br />  end<br /><br />--------------------<br />DateTime ()<br />   <em>(+0 other overloads)</em><br />DateTime(ticks: int64) : DateTime<br />   <em>(+0 other overloads)</em><br />DateTime(ticks: int64, kind: DateTimeKind) : DateTime<br />   <em>(+0 other overloads)</em><br />DateTime(year: int, month: int, day: int) : DateTime<br />   <em>(+0 other overloads)</em><br />DateTime(year: int, month: int, day: int, calendar: Globalization.Calendar) : DateTime<br />   <em>(+0 other overloads)</em><br />DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int) : DateTime<br />   <em>(+0 other overloads)</em><br />DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, kind: DateTimeKind) : DateTime<br />   <em>(+0 other overloads)</em><br />DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, calendar: Globalization.Calendar) : DateTime<br />   <em>(+0 other overloads)</em><br />DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int) : DateTime<br />   <em>(+0 other overloads)</em><br />DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, kind: DateTimeKind) : DateTime<br />   <em>(+0 other overloads)</em></div>
<div class="tip" id="fs22">property DateTime.Now: DateTime</div>
<div class="tip" id="fs23">Multiple items<br />type StringBuilder =<br />  new : unit -> StringBuilder + 5 overloads<br />  member Append : value:string -> StringBuilder + 19 overloads<br />  member AppendFormat : format:string * arg0:obj -> StringBuilder + 7 overloads<br />  member AppendLine : unit -> StringBuilder + 1 overload<br />  member Capacity : int with get, set<br />  member Chars : int -> char with get, set<br />  member Clear : unit -> StringBuilder<br />  member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit<br />  member EnsureCapacity : capacity:int -> int<br />  member Equals : sb:StringBuilder -> bool<br />  ...<br /><br />--------------------<br />StringBuilder() : StringBuilder<br />StringBuilder(capacity: int) : StringBuilder<br />StringBuilder(value: string) : StringBuilder<br />StringBuilder(value: string, capacity: int) : StringBuilder<br />StringBuilder(capacity: int, maxCapacity: int) : StringBuilder<br />StringBuilder(value: string, startIndex: int, length: int, capacity: int) : StringBuilder</div>
<div class="tip" id="fs24">val ignore : value:'T -> unit</div>
<div class="tip" id="fs25">module Printf<br /><br />from Microsoft.FSharp.Core</div>
<div class="tip" id="fs26">val kbprintf : continuation:(unit -> 'Result) -> builder:StringBuilder -> format:Printf.BuilderFormat<'T,'Result> -> 'T</div>
<div class="tip" id="fs27">module Option<br /><br />from Microsoft.FSharp.Core</div>
<div class="tip" id="fs28">val isNone : option:'T option -> bool</div>
<div class="tip" id="fs29">val get : option:'T option -> 'T</div>
<div class="tip" id="fs30">type Console =<br />  static member BackgroundColor : ConsoleColor with get, set<br />  static member Beep : unit -> unit + 1 overload<br />  static member BufferHeight : int with get, set<br />  static member BufferWidth : int with get, set<br />  static member CapsLock : bool<br />  static member Clear : unit -> unit<br />  static member CursorLeft : int with get, set<br />  static member CursorSize : int with get, set<br />  static member CursorTop : int with get, set<br />  static member CursorVisible : bool with get, set<br />  ...</div>
<div class="tip" id="fs31">property Console.ForegroundColor: ConsoleColor</div>
<div class="tip" id="fs32">type ConsoleColor =<br />  | Black = 0<br />  | DarkBlue = 1<br />  | DarkGreen = 2<br />  | DarkCyan = 3<br />  | DarkRed = 4<br />  | DarkMagenta = 5<br />  | DarkYellow = 6<br />  | Gray = 7<br />  | DarkGray = 8<br />  | Blue = 9<br />  ...</div>
<div class="tip" id="fs33">field ConsoleColor.Red: ConsoleColor = 12</div>
<div class="tip" id="fs34">Console.WriteLine() : unit<br />   <em>(+0 other overloads)</em><br />Console.WriteLine(value: string) : unit<br />   <em>(+0 other overloads)</em><br />Console.WriteLine(value: obj) : unit<br />   <em>(+0 other overloads)</em><br />Console.WriteLine(value: uint64) : unit<br />   <em>(+0 other overloads)</em><br />Console.WriteLine(value: int64) : unit<br />   <em>(+0 other overloads)</em><br />Console.WriteLine(value: uint32) : unit<br />   <em>(+0 other overloads)</em><br />Console.WriteLine(value: int) : unit<br />   <em>(+0 other overloads)</em><br />Console.WriteLine(value: float32) : unit<br />   <em>(+0 other overloads)</em><br />Console.WriteLine(value: float) : unit<br />   <em>(+0 other overloads)</em><br />Console.WriteLine(value: decimal) : unit<br />   <em>(+0 other overloads)</em></div>
<div class="tip" id="fs35">Multiple items<br />val seq : sequence:seq<'T> -> seq<'T><br /><br />--------------------<br />type seq<'T> = Collections.Generic.IEnumerable<'T></div>
<div class="tip" id="fs36">module Seq<br /><br />from Microsoft.FSharp.Collections</div>
<div class="tip" id="fs37">val cache : source:seq<'T> -> seq<'T></div>
<div class="tip" id="fs38">val iter : action:('T -> unit) -> source:seq<'T> -> unit</div>
<div class="tip" id="fs39">Multiple items<br />val string : value:'T -> string<br /><br />--------------------<br />type string = String</div>
<div class="tip" id="fs40">val infinity : float</div>
<div class="tip" id="fs41">Multiple items<br />module Map<br /><br />from Microsoft.FSharp.Collections<br /><br />--------------------<br />type Map<'Key,'Value (requires comparison)> =<br />  interface IReadOnlyDictionary<'Key,'Value><br />  interface IReadOnlyCollection<KeyValuePair<'Key,'Value>><br />  interface IEnumerable<br />  interface IComparable<br />  interface IEnumerable<KeyValuePair<'Key,'Value>><br />  interface ICollection<KeyValuePair<'Key,'Value>><br />  interface IDictionary<'Key,'Value><br />  new : elements:seq<'Key * 'Value> -> Map<'Key,'Value><br />  member Add : key:'Key * value:'Value -> Map<'Key,'Value><br />  member ContainsKey : key:'Key -> bool<br />  ...<br /><br />--------------------<br />new : elements:seq<'Key * 'Value> -> Map<'Key,'Value></div>
<div class="tip" id="fs42">val empty<'Key,'T (requires comparison)> : Map<'Key,'T> (requires comparison)</div>
<div class="tip" id="fs43">type unit = Unit</div>
<div class="tip" id="fs44">val add : key:'Key -> value:'T -> table:Map<'Key,'T> -> Map<'Key,'T> (requires comparison)</div>
<div class="tip" id="fs45">val find : key:'Key -> table:Map<'Key,'T> -> 'T (requires comparison)</div>
Choosing Simplicity - not the easy option
2017-02-28T00:00:00+00:00
http://anthonylloyd.github.io/blog/2017/02/28/choosing-simplicity
<p>This is a short post on a few recent events that are examples of a common idea.</p>
<h3><a name="Expecto-testing-library-vs-testing-framework" class="anchor" href="#Expecto-testing-library-vs-testing-framework">Expecto - testing library vs testing framework</a></h3>
<p><a href="https://github.com/haf/expecto">Expecto</a> is a new testing library that has chosen a different approach to existing testing frameworks.
It's designed as a library to be used in a testing exe project instead of code written to run inside a framework.
Tomas Petricek hits the nail on head with his <a href="http://tomasp.net/blog/2015/library-frameworks/">post</a> on why frameworks are limited compared to libraries.</p>
<p>In <a href="https://github.com/haf/expecto">Expecto</a>, tests are constructed as values, so normal code is used to filter, parameterise, reuse and compose them.
By backing up and choosing a simpler evolutionary path it's hoped the library can go further than current testing frameworks.
This was not the easy option and several components like the Visual Studio Plugin and Visual Studio Code integration have had to be built.
There has also been some scepticism that a new approach is needed.</p>
<p>Because of its simplicity <a href="https://github.com/haf/expecto">Expecto</a> already has some unique features:</p>
<ul>
<li>The library itself is easy to test since it can be run inside tests.</li>
<li>Structuring tests in lists and trees enables more flexible configuration.</li>
<li>Tests can be run in parallel, some can be globally sequential, and some can be sequential in small groups.</li>
<li>Stress testing can be used to randomly run a test suite in parallel for a long period to catch rare bugs and memory leaks.</li>
<li>Fast statistical relative performance tests can be run as part of normal testing.</li>
</ul>
<h3><a name="Serialization-library-vs-hand-coding" class="anchor" href="#Serialization-library-vs-hand-coding">Serialization - library vs hand coding</a></h3>
<p>On one of my own event sourcing projects I've taken the decision to hand code the serialization and not use a library.</p>
<p>You've done what? You are crazy.</p>
<p>I need to make sure the serialization will cope with schema migration and always be backwardly compatible.
I also have specific serialization compression I want to make use of e.g. <a href="https://github.com/Genbox/CSharpFastPFOR">FastPFOR</a>.</p>
<p>I took inspiration from the Haskell <a href="https://hackage.haskell.org/package/cereal-0.5.4.0/docs/Data-Serialize.html">Data.Serialize</a> library.</p>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="k">type</span> <span onmouseout="hideTip(event, 'fs2', 2)" onmouseover="showTip(event, 'fs2', 2)" class="rt">Resize</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs3', 3)" onmouseover="showTip(event, 'fs3', 3)" class="vt">byte</span><span class="pn">[</span><span class="pn">]</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs4', 4)" onmouseover="showTip(event, 'fs4', 4)" class="vt">int</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs3', 5)" onmouseover="showTip(event, 'fs3', 5)" class="vt">byte</span><span class="pn">[</span><span class="pn">]</span>
<span class="k">type</span> <span onmouseout="hideTip(event, 'fs5', 6)" onmouseover="showTip(event, 'fs5', 6)" class="rt">State</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs3', 7)" onmouseover="showTip(event, 'fs3', 7)" class="vt">byte</span><span class="pn">[</span><span class="pn">]</span> <span class="pn">*</span> <span onmouseout="hideTip(event, 'fs4', 8)" onmouseover="showTip(event, 'fs4', 8)" class="vt">int</span>
<span class="k">type</span> <span class="ta">'</span><span class="id">a</span> <span onmouseout="hideTip(event, 'fs6', 9)" onmouseover="showTip(event, 'fs6', 9)" class="rt">SerializeGet</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs5', 10)" onmouseover="showTip(event, 'fs5', 10)" class="rt">State</span> <span class="k">-></span> <span class="ta">'</span><span class="id">a</span> <span class="pn">*</span> <span onmouseout="hideTip(event, 'fs4', 11)" onmouseover="showTip(event, 'fs4', 11)" class="vt">int</span>
<span class="k">type</span> <span onmouseout="hideTip(event, 'fs7', 12)" onmouseover="showTip(event, 'fs7', 12)" class="rt">SerializePut</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs2', 13)" onmouseover="showTip(event, 'fs2', 13)" class="rt">Resize</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs5', 14)" onmouseover="showTip(event, 'fs5', 14)" class="rt">State</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs5', 15)" onmouseover="showTip(event, 'fs5', 15)" class="rt">State</span>
<span class="k">type</span> <span class="ta">'</span><span class="id">a</span> <span onmouseout="hideTip(event, 'fs8', 16)" onmouseover="showTip(event, 'fs8', 16)" class="rt">Serialize</span> <span class="o">=</span>
<span class="pn">{</span>
<span onmouseout="hideTip(event, 'fs9', 17)" onmouseover="showTip(event, 'fs9', 17)" class="fn">Put</span><span class="pn">:</span> <span class="ta">'</span><span class="id">a</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs7', 18)" onmouseover="showTip(event, 'fs7', 18)" class="rt">SerializePut</span>
<span onmouseout="hideTip(event, 'fs10', 19)" onmouseover="showTip(event, 'fs10', 19)" class="fn">Get</span><span class="pn">:</span> <span class="ta">'</span><span class="id">a</span> <span onmouseout="hideTip(event, 'fs6', 20)" onmouseover="showTip(event, 'fs6', 20)" class="rt">SerializeGet</span>
<span class="pn">}</span>
</code></pre>
<p>Monads can be made for <code>SerializePut</code> and <code>SerializeGet</code>.
This makes composing a type serializer from more primitive serializers very easy.
Essentially after the primitives have been built it takes only two lines of simple code per field.</p>
<p>Using a great testing library (see what I did there) serialization is surprisingly easy to test thoroughly.
Property based testing is used to ensure all serialization roundtrips correctly.
This includes tests to cover schema migration and backward compatibility.</p>
<p>For the cost of a little extra code on schema change a simple serialization library can be built.
It has the advantage of not needing any reflection or code generation.
Also, because it is bespoke it should have great performance and produce smaller messages.</p>
<h3><a name="Support-for-type-classes-and-HKTs-in-F" class="anchor" href="#Support-for-type-classes-and-HKTs-in-F">Support for type classes and HKTs in F#</a></h3>
<p>Don Syme recently <a href="https://github.com/fsharp/fslang-suggestions/issues/243#issuecomment-282455245">commented</a> on adding type classes and HKTs to F#.</p>
<p>Like many others I've never designed a programming language but that is not going to stop me commenting on its evolution.
From what I can see language design has a greater proportion of irreversible decisions than other areas of software engineering.
It's well known that people spend too much time on reversible decisions and too little on irreversible ones.</p>
<p>Sometimes you need to simmer an idea down and add it at the right point to get the tastiest result.</p>
<p>There is some pressure from the community to get something in after it was announced that C# was exploring adding type classes.
The decision to hold off can't be an easy one.</p>
<p>Personally, I'm happy to wait if it ensures F# is kept as simple and coherent as possible.</p>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p>Simple solutions are easier to understand, generalise more naturally, and are more amenable to change.</p>
<p>Simplicity is not the easy option but it is worth fighting for.</p>
<div class="tip" id="fs1">module Main</div>
<div class="tip" id="fs2">type Resize = byte [] -> int -> byte []</div>
<div class="tip" id="fs3">Multiple items<br />val byte : value:'T -> byte (requires member op_Explicit)<br /><br />--------------------<br />type byte = System.Byte</div>
<div class="tip" id="fs4">Multiple items<br />val int : value:'T -> int (requires member op_Explicit)<br /><br />--------------------<br />type int = int32<br /><br />--------------------<br />type int<'Measure> = int</div>
<div class="tip" id="fs5">type State = byte [] * int</div>
<div class="tip" id="fs6">type 'a SerializeGet = State -> 'a * int</div>
<div class="tip" id="fs7">type SerializePut = Resize -> State -> State</div>
<div class="tip" id="fs8">type 'a Serialize =<br />  {Put: 'a -> SerializePut;<br />   Get: 'a SerializeGet;}</div>
<div class="tip" id="fs9">Serialize.Put: 'a -> SerializePut</div>
<div class="tip" id="fs10">Serialize.Get: 'a SerializeGet</div>
Functional Event Sourcing meets The Elm Architecture
2016-11-27T00:00:00+00:00
http://anthonylloyd.github.io/blog/2016/11/27/event-sourcing
<p>This post is part of the <a href="https://sergeytihon.wordpress.com/2016/10/23/f-advent-calendar-in-english-2016/">F# Advent Calendar 2016</a> series.</p>
<p>One of the highlights of the year for me was the <a href="http://elm-lang.org/blog/farewell-to-frp">farewell to FRP</a> post by Evan Czaplicki.
For a long time, I've been looking for a simple functional alternative to the MVC UI models.</p>
<p>There are a number of <a href="https://en.wikipedia.org/wiki/Functional_reactive_programming">FRP</a> alternatives but they all have limitations.
They use signals heavily and many have inherent memory leak issues.</p>
<p>Czaplicki removed signals, simplifying the model dramatically. It resulted in something truly beautiful. A simple and composable way of building UIs.</p>
<p>Event Sourcing is also a compelling pattern I have found very useful. In some domains like accounting it is a perfect fit.</p>
<p>This post explores how Functional Event Sourcing fits with <a href="https://guide.elm-lang.org/architecture/index.html">the Elm Architecture</a> covered in a previous <a href="/blog/2016/06/20/fsharp-elm-part1">post</a>.
A combined festive WPF application is developed to streamline Santa's workload. The application code can be found <a href="https://github.com/AnthonyLloyd/Event">here</a>.</p>
<h2><a name="Functional-Event-Sourcing" class="anchor" href="#Functional-Event-Sourcing">Functional Event Sourcing</a></h2>
<h3><a name="Event" class="anchor" href="#Event">Event</a></h3>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="k">type</span> <span onmouseout="hideTip(event, 'fs11', 11)" onmouseover="showTip(event, 'fs11', 11)" class="rt">EventID</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs11', 12)" onmouseover="showTip(event, 'fs11', 12)" class="uc">EventID</span> <span class="k">of</span> <span class="id">time</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs12', 13)" onmouseover="showTip(event, 'fs12', 13)" class="vt">DateTime</span> <span class="pn">*</span> <span class="id">user</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs5', 14)" onmouseover="showTip(event, 'fs5', 14)" class="rt">UserID</span>
</code></pre>
<p>An event is something that happens at a specific point.
In physics an event is a change that happens at a point in spacetime.
In event sourcing the point is a unique identifier of the event.</p>
<p>In most systems this can just be the time and user who created the event.
This <code>EventID</code> can also include additional data required to make it unique.</p>
<p>The example application uses <code>Stopwatch</code> to increase the precision of <code>DateTime</code>.
The application also ensures each <code>EventID</code> time is unique.
NTP servers could also be used to calibrate the application if a comparison of time between different machines is required.</p>
<p>As well as being a unique identifier of the event the <code>EventID</code> also satisfies all the data requirement for audit.</p>
<h3><a name="Aggregate" class="anchor" href="#Aggregate">Aggregate</a></h3>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="k">type</span> <span class="ta">'</span><span class="id">Aggregate</span> <span onmouseout="hideTip(event, 'fs13', 15)" onmouseover="showTip(event, 'fs13', 15)" class="rt">ID</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs14', 16)" onmouseover="showTip(event, 'fs14', 16)" class="uc">Created</span> <span class="k">of</span> <span onmouseout="hideTip(event, 'fs11', 17)" onmouseover="showTip(event, 'fs11', 17)" class="rt">EventID</span>
<span class="k">type</span> <span class="ta">'</span><span class="id">Aggregate</span> <span onmouseout="hideTip(event, 'fs15', 18)" onmouseover="showTip(event, 'fs15', 18)" class="rt">Events</span> <span class="o">=</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs11', 19)" onmouseover="showTip(event, 'fs11', 19)" class="rt">EventID</span> <span class="pn">*</span> <span class="ta">'</span><span class="id">Aggregate</span> <span onmouseout="hideTip(event, 'fs2', 20)" onmouseover="showTip(event, 'fs2', 20)" class="rt">list1</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs2', 21)" onmouseover="showTip(event, 'fs2', 21)" class="rt">list1</span>
</code></pre>
<p>An aggregate is a collection of events that are bound together by a root entity.
External entities can only hold a reference to the root entity identifier.
An aggregate is a unit of consistency that has atomicity and autonomy.</p>
<p>In the example application we have the following domain model.
Each case represents a possible change to the aggregate.</p>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="k">type</span> <span onmouseout="hideTip(event, 'fs16', 22)" onmouseover="showTip(event, 'fs16', 22)" class="vt">Work</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs17', 23)" onmouseover="showTip(event, 'fs17', 23)" class="vt">uint16</span>
<span class="k">type</span> <span onmouseout="hideTip(event, 'fs18', 24)" onmouseover="showTip(event, 'fs18', 24)" class="vt">Age</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs19', 25)" onmouseover="showTip(event, 'fs19', 25)" class="vt">byte</span>
<span class="k">type</span> <span onmouseout="hideTip(event, 'fs20', 26)" onmouseover="showTip(event, 'fs20', 26)" class="rt">Behaviour</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs21', 27)" onmouseover="showTip(event, 'fs21', 27)" class="uc">Good</span> <span class="pn">|</span> <span onmouseout="hideTip(event, 'fs22', 28)" onmouseover="showTip(event, 'fs22', 28)" class="uc">Mixed</span> <span class="pn">|</span> <span onmouseout="hideTip(event, 'fs23', 29)" onmouseover="showTip(event, 'fs23', 29)" class="uc">Bad</span>
<span class="k">type</span> <span onmouseout="hideTip(event, 'fs24', 30)" onmouseover="showTip(event, 'fs24', 30)" class="rt">Toy</span> <span class="o">=</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs25', 31)" onmouseover="showTip(event, 'fs25', 31)" class="uc">Name</span> <span class="k">of</span> <span onmouseout="hideTip(event, 'fs26', 32)" onmouseover="showTip(event, 'fs26', 32)" class="rt">string</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs27', 33)" onmouseover="showTip(event, 'fs27', 33)" class="uc">AgeRange</span> <span class="k">of</span> <span class="id">lo</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs18', 34)" onmouseover="showTip(event, 'fs18', 34)" class="vt">Age</span> <span class="pn">*</span> <span class="id">hi</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs18', 35)" onmouseover="showTip(event, 'fs18', 35)" class="vt">Age</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs28', 36)" onmouseover="showTip(event, 'fs28', 36)" class="uc">WorkRequired</span> <span class="k">of</span> <span onmouseout="hideTip(event, 'fs16', 37)" onmouseover="showTip(event, 'fs16', 37)" class="vt">Work</span>
<span class="k">type</span> <span onmouseout="hideTip(event, 'fs29', 38)" onmouseover="showTip(event, 'fs29', 38)" class="rt">Elf</span> <span class="o">=</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs30', 39)" onmouseover="showTip(event, 'fs30', 39)" class="uc">Name</span> <span class="k">of</span> <span onmouseout="hideTip(event, 'fs26', 40)" onmouseover="showTip(event, 'fs26', 40)" class="rt">string</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs31', 41)" onmouseover="showTip(event, 'fs31', 41)" class="uc">WorkRate</span> <span class="k">of</span> <span onmouseout="hideTip(event, 'fs16', 42)" onmouseover="showTip(event, 'fs16', 42)" class="vt">Work</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs32', 43)" onmouseover="showTip(event, 'fs32', 43)" class="uc">Making</span> <span class="k">of</span> <span onmouseout="hideTip(event, 'fs24', 44)" onmouseover="showTip(event, 'fs24', 44)" class="rt">Toy</span> <span onmouseout="hideTip(event, 'fs13', 45)" onmouseover="showTip(event, 'fs13', 45)" class="rt">ID</span> <span onmouseout="hideTip(event, 'fs33', 46)" onmouseover="showTip(event, 'fs33', 46)" class="rt">option</span>
<span class="k">type</span> <span onmouseout="hideTip(event, 'fs34', 47)" onmouseover="showTip(event, 'fs34', 47)" class="rt">Kid</span> <span class="o">=</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs35', 48)" onmouseover="showTip(event, 'fs35', 48)" class="uc">Name</span> <span class="k">of</span> <span onmouseout="hideTip(event, 'fs26', 49)" onmouseover="showTip(event, 'fs26', 49)" class="rt">string</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs36', 50)" onmouseover="showTip(event, 'fs36', 50)" class="uc">Age</span> <span class="k">of</span> <span onmouseout="hideTip(event, 'fs36', 51)" onmouseover="showTip(event, 'fs36', 51)" class="vt">Age</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs37', 52)" onmouseover="showTip(event, 'fs37', 52)" class="uc">Behaviour</span> <span class="k">of</span> <span onmouseout="hideTip(event, 'fs37', 53)" onmouseover="showTip(event, 'fs37', 53)" class="rt">Behaviour</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs38', 54)" onmouseover="showTip(event, 'fs38', 54)" class="uc">WishList</span> <span class="k">of</span> <span onmouseout="hideTip(event, 'fs24', 55)" onmouseover="showTip(event, 'fs24', 55)" class="rt">Toy</span> <span onmouseout="hideTip(event, 'fs13', 56)" onmouseover="showTip(event, 'fs13', 56)" class="rt">ID</span> <span onmouseout="hideTip(event, 'fs8', 57)" onmouseover="showTip(event, 'fs8', 57)" class="rt">SetEvent</span>
</code></pre>
<p>Most of the events are simple field changes but events such as <code>Recalled</code> for <code>Toy</code> are possible.</p>
<p>The rules for domain model schema migration and data serialization are</p>
<ul>
<li>Cases cannot be removed or their data type changed</li>
<li>Cases cannot be reordered</li>
<li>Cases can be added (legacy code needs to ignore these)</li>
<li>Cases can be renamed</li>
</ul>
<p><a href="http://mbraceproject.github.io/FsPickler/">FsPickler</a> can be configured to comply with these rules, making it easy to serialize events.</p>
<h3><a name="Store" class="anchor" href="#Store">Store</a></h3>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="k">type</span> <span class="ta">'</span><span class="id">Aggregate</span> <span onmouseout="hideTip(event, 'fs39', 58)" onmouseover="showTip(event, 'fs39', 58)" class="rt">MemoryStore</span> <span class="o">=</span>
<span class="pn">{</span>
<span onmouseout="hideTip(event, 'fs40', 59)" onmouseover="showTip(event, 'fs40', 59)" class="id">Latest</span><span class="pn">:</span> <span onmouseout="hideTip(event, 'fs41', 60)" onmouseover="showTip(event, 'fs41', 60)" class="rt">Map</span><span class="pn"><</span><span class="ta">'</span><span class="id">Aggregate</span> <span onmouseout="hideTip(event, 'fs13', 61)" onmouseover="showTip(event, 'fs13', 61)" class="rt">ID</span><span class="pn">,</span> <span class="ta">'</span><span class="id">Aggregate</span> <span onmouseout="hideTip(event, 'fs15', 62)" onmouseover="showTip(event, 'fs15', 62)" class="rt">Events</span><span class="pn">></span>
<span onmouseout="hideTip(event, 'fs42', 63)" onmouseover="showTip(event, 'fs42', 63)" class="id">Observers</span><span class="pn">:</span> <span onmouseout="hideTip(event, 'fs43', 64)" onmouseover="showTip(event, 'fs43', 64)" class="if">IObserver</span><span class="pn"><</span><span onmouseout="hideTip(event, 'fs41', 65)" onmouseover="showTip(event, 'fs41', 65)" class="rt">Map</span><span class="pn"><</span><span class="ta">'</span><span class="id">Aggregate</span> <span onmouseout="hideTip(event, 'fs13', 66)" onmouseover="showTip(event, 'fs13', 66)" class="rt">ID</span><span class="pn">,</span> <span class="ta">'</span><span class="id">Aggregate</span> <span onmouseout="hideTip(event, 'fs15', 67)" onmouseover="showTip(event, 'fs15', 67)" class="rt">Events</span><span class="pn">></span><span class="pn">></span> <span onmouseout="hideTip(event, 'fs4', 68)" onmouseover="showTip(event, 'fs4', 68)" class="rt">list</span>
<span class="pn">}</span>
</code></pre>
<p>Stores are the databases of event sourcing.
They can be in memory, remote or disconnected for example.</p>
<p>Many different concurrency models are possible.
In the example application we have linear event sourcing with optimistic concurrency which is the simplest and corresponds to most relational database applications.</p>
<p>More fine grained concurrency is possible and <code>Making</code> on <code>Elf</code> would be a good candidate as only the Santa process changes this.
Advanced concurrency models are also possible with event sourcing where events are designed to commute such as <a href="https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type">CRDTs</a>.
These enable disconnected systems. Git is an example of a successful disconnected system.</p>
<h3><a name="Benefits-of-functional-event-sourcing" class="anchor" href="#Benefits-of-functional-event-sourcing">Benefits of functional event sourcing</a></h3>
<ul>
<li>The only model that does not lose data</li>
<li>Built in audit log</li>
<li>View any previously generated report</li>
<li>Temporal querying</li>
<li>Preserves history, questions not yet asked</li>
<li>Well defined and simple schema migration</li>
<li>Zero data persistence code, no ORM problem</li>
<li>Easier testing - regression, time travel debug</li>
</ul>
<h2><a name="Example-Application" class="anchor" href="#Example-Application">Example Application</a></h2>
<p>The application has two background processes running continuously.
The first is the kids process that randomly adds and removes toys to the kids' Christmas wishlists.
The second is the Santa process that assigns free elfs to make toys in the priority order of kid behaviour and request time.</p>
<p>All the screens update in realtime and any of the entities in the domain can be edited.
All the entity edit screens have validation at both the field and aggregate level.
A field editor Elm app was reused across all these fields.</p>
<p>The previous F# Elm implementation was extended to include subscriptions and commands.
Minimal UI styling functionality was also added.</p>
<p><img src="/public/event/Santa.png" alt="Santa's Summary" title="Santa's Summary" /></p>
<table style="border:0px">
<tr>
<td style="background-color:white;border:0px"><img src="/public/event/Kid.png" title="Kid Screen" width="340px" height="434px"/></td>
<td style="background-color:white;border:0px"><img src="/public/event/Toy.png" title="Toy Screen" width="340px" height="434px"/></td>
<td style="background-color:white;border:0px"><img src="/public/event/Elf.png" title="Elf Screen" width="340px" height="434px"/></td>
</tr>
</table>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p>This turns out to be quite a complicated problem we have solved.
It would be interesting to see a more traditional solution in OO and a relational data model.
I can only imagine that both the domain model and code would be much more complicated.</p>
<p>One caveat with event sourcing would be that cross aggregate transactions are not possible.
This may take a little thinking to become comfortable with.
It is possible to express two phase commits explicitly in the domain model.
Being explicit about these may also tease out the correct business requirements and lead to a better solution.</p>
<p>Functional Event Sourcing fits naturally with the subscription and command model in Elm.
Time travel debug and easy regression are features of both patterns and work well together.
The patterns result in a highly type safe and testable system.</p>
<p>I would recommend functional event sourcing in any application where strong audit or schema evolution are a requirement.
Linear event sourcing, optimistic concurrency and persisting each type to a single database table would be a natural starting point.</p>
<p>Hopefully F# will get Santa's follow up present delivery project. Happy holidays!</p>
<div class="tip" id="fs1">namespace System</div>
<div class="tip" id="fs2">type 'a list1 = | List1 of 'a list<br /><em><br /><br /> A non-empty list</em></div>
<div class="tip" id="fs3">union case list1.List1: 'a list -> 'a list1</div>
<div class="tip" id="fs4">type 'T list = List<'T></div>
<div class="tip" id="fs5">type UserID = | User of int</div>
<div class="tip" id="fs6">union case UserID.User: int -> UserID</div>
<div class="tip" id="fs7">Multiple items<br />val int : value:'T -> int (requires member op_Explicit)<br /><br />--------------------<br />type int = int32<br /><br />--------------------<br />type int<'Measure> = int</div>
<div class="tip" id="fs8">type 'a SetEvent =<br />  | SetAdd of 'a<br />  | SetRemove of 'a</div>
<div class="tip" id="fs9">union case SetEvent.SetAdd: 'a -> 'a SetEvent</div>
<div class="tip" id="fs10">union case SetEvent.SetRemove: 'a -> 'a SetEvent</div>
<div class="tip" id="fs11">Multiple items<br />union case EventID.EventID: time: DateTime * user: UserID -> EventID<br /><br />--------------------<br />type EventID = | EventID of time: DateTime * user: UserID</div>
<div class="tip" id="fs12">Multiple items<br />type DateTime =<br />  struct<br />    new : ticks:int64 -> DateTime + 10 overloads<br />    member Add : value:TimeSpan -> DateTime<br />    member AddDays : value:float -> DateTime<br />    member AddHours : value:float -> DateTime<br />    member AddMilliseconds : value:float -> DateTime<br />    member AddMinutes : value:float -> DateTime<br />    member AddMonths : months:int -> DateTime<br />    member AddSeconds : value:float -> DateTime<br />    member AddTicks : value:int64 -> DateTime<br />    member AddYears : value:int -> DateTime<br />    ...<br />  end<br /><br />--------------------<br />DateTime ()<br />   <em>(+0 other overloads)</em><br />DateTime(ticks: int64) : DateTime<br />   <em>(+0 other overloads)</em><br />DateTime(ticks: int64, kind: DateTimeKind) : DateTime<br />   <em>(+0 other overloads)</em><br />DateTime(year: int, month: int, day: int) : DateTime<br />   <em>(+0 other overloads)</em><br />DateTime(year: int, month: int, day: int, calendar: Globalization.Calendar) : DateTime<br />   <em>(+0 other overloads)</em><br />DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int) : DateTime<br />   <em>(+0 other overloads)</em><br />DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, kind: DateTimeKind) : DateTime<br />   <em>(+0 other overloads)</em><br />DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, calendar: Globalization.Calendar) : DateTime<br />   <em>(+0 other overloads)</em><br />DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int) : DateTime<br />   <em>(+0 other overloads)</em><br />DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, kind: DateTimeKind) : DateTime<br />   <em>(+0 other overloads)</em></div>
<div class="tip" id="fs13">type 'Aggregate ID = | Created of EventID</div>
<div class="tip" id="fs14">union case ID.Created: EventID -> 'Aggregate ID</div>
<div class="tip" id="fs15">type 'Aggregate Events = (EventID * 'Aggregate list1) list1</div>
<div class="tip" id="fs16">type Work = uint16</div>
<div class="tip" id="fs17">Multiple items<br />val uint16 : value:'T -> uint16 (requires member op_Explicit)<br /><br />--------------------<br />type uint16 = UInt16</div>
<div class="tip" id="fs18">type Age = byte</div>
<div class="tip" id="fs19">Multiple items<br />val byte : value:'T -> byte (requires member op_Explicit)<br /><br />--------------------<br />type byte = Byte</div>
<div class="tip" id="fs20">type Behaviour =<br />  | Good<br />  | Mixed<br />  | Bad</div>
<div class="tip" id="fs21">union case Behaviour.Good: Behaviour</div>
<div class="tip" id="fs22">union case Behaviour.Mixed: Behaviour</div>
<div class="tip" id="fs23">union case Behaviour.Bad: Behaviour</div>
<div class="tip" id="fs24">type Toy =<br />  | Name of string<br />  | AgeRange of lo: Age * hi: Age<br />  | WorkRequired of Work</div>
<div class="tip" id="fs25">union case Toy.Name: string -> Toy</div>
<div class="tip" id="fs26">Multiple items<br />val string : value:'T -> string<br /><br />--------------------<br />type string = String</div>
<div class="tip" id="fs27">union case Toy.AgeRange: lo: Age * hi: Age -> Toy</div>
<div class="tip" id="fs28">union case Toy.WorkRequired: Work -> Toy</div>
<div class="tip" id="fs29">type Elf =<br />  | Name of string<br />  | WorkRate of Work<br />  | Making of Toy ID option</div>
<div class="tip" id="fs30">union case Elf.Name: string -> Elf</div>
<div class="tip" id="fs31">union case Elf.WorkRate: Work -> Elf</div>
<div class="tip" id="fs32">union case Elf.Making: Toy ID option -> Elf</div>
<div class="tip" id="fs33">type 'T option = Option<'T></div>
<div class="tip" id="fs34">type Kid =<br />  | Name of string<br />  | Age of Age<br />  | Behaviour of Behaviour<br />  | WishList of Toy ID SetEvent</div>
<div class="tip" id="fs35">union case Kid.Name: string -> Kid</div>
<div class="tip" id="fs36">Multiple items<br />union case Kid.Age: Age -> Kid<br /><br />--------------------<br />type Age = byte</div>
<div class="tip" id="fs37">Multiple items<br />union case Kid.Behaviour: Behaviour -> Kid<br /><br />--------------------<br />type Behaviour =<br />  | Good<br />  | Mixed<br />  | Bad</div>
<div class="tip" id="fs38">union case Kid.WishList: Toy ID SetEvent -> Kid</div>
<div class="tip" id="fs39">type 'Aggregate MemoryStore =<br />  {Latest: Map<'Aggregate ID,'Aggregate Events>;<br />   Observers: IObserver<Map<'Aggregate ID,'Aggregate Events>> list;}</div>
<div class="tip" id="fs40">MemoryStore.Latest: Map<'Aggregate ID,'Aggregate Events></div>
<div class="tip" id="fs41">Multiple items<br />module Map<br /><br />from Microsoft.FSharp.Collections<br /><br />--------------------<br />type Map<'Key,'Value (requires comparison)> =<br />  interface IReadOnlyDictionary<'Key,'Value><br />  interface IReadOnlyCollection<KeyValuePair<'Key,'Value>><br />  interface IEnumerable<br />  interface IComparable<br />  interface IEnumerable<KeyValuePair<'Key,'Value>><br />  interface ICollection<KeyValuePair<'Key,'Value>><br />  interface IDictionary<'Key,'Value><br />  new : elements:seq<'Key * 'Value> -> Map<'Key,'Value><br />  member Add : key:'Key * value:'Value -> Map<'Key,'Value><br />  member ContainsKey : key:'Key -> bool<br />  ...<br /><br />--------------------<br />new : elements:seq<'Key * 'Value> -> Map<'Key,'Value></div>
<div class="tip" id="fs42">MemoryStore.Observers: IObserver<Map<'Aggregate ID,'Aggregate Events>> list</div>
<div class="tip" id="fs43">type IObserver<'T> =<br />  member OnCompleted : unit -> unit<br />  member OnError : error:Exception -> unit<br />  member OnNext : value:'T -> unit</div>
Get MAD with Outliers with an Improved Median Function
2016-10-21T00:00:00+01:00
http://anthonylloyd.github.io/blog/2016/10/21/MAD-Outliers
<p>This post presents a more robust method of detecting outliers in sample data than commonly used.
The method is based on the median and an optimised F# median function is developed.</p>
<h2><a name="Background" class="anchor" href="#Background">Background</a></h2>
<p><a href="https://www.researchgate.net/publication/256752600_Detecting_outliers_Do_not_use_standard_deviation_around_the_mean_use_absolute_deviation_around_the_median">Researchers</a> most commonly use standard deviation around the mean to detect outliers.
This is a problem because the mean and standard deviation give greater weight to extreme data points.</p>
<p>The mean is the point that minimises the sum of the square deviations, whereas the median is the point that minimises the sum of the absolute deviations.
The median and <a href="https://en.wikipedia.org/wiki/Median_absolute_deviation">median absolute deviation</a> give a more robust measure of statistical dispersion and are more resilient to outliers.</p>
<p>When a politician says average wages are increasing be sure to check the median is being reported.</p>
<h2><a name="Median-Absolute-Deviation" class="anchor" href="#Median-Absolute-Deviation">Median Absolute Deviation</a></h2>
<p>The <a href="https://en.wikipedia.org/wiki/Median">median</a> is the value separating the higher half of the sample from the lower half.</p>
<p>The <a href="https://en.wikipedia.org/wiki/Median_absolute_deviation">median absolute deviation</a> is defined as</p>
<p><span class="math">\[\operatorname{MAD} = \operatorname{median}\left(\left|x_i - \operatorname{median}(x_i)\right|\right)\]</span></p>
<p>Outliers can be identified as points that are outside a fixed multiple of the median absolute deviation from the median. Recommended values for this multiple are 2.5 or 3.</p>
<h2><a name="Median-and-MAD-code" class="anchor" href="#Median-and-MAD-code">Median and MAD code</a></h2>
<p>The following median function makes use of the <a href="http://zabrodskyvlada.byethost10.com/aat/a_modi.html">MODIFIND</a> algorithm by Vladimir Zabrodsky.
It provides a 20-30% performance improvement over the <a href="https://en.wikipedia.org/wiki/Quickselect">Quickselect</a> algorithm.</p>
<p>The array while loops allow equality which improves performance when there is duplication and ordering in the data.
The <code>selectInPlace</code> function has also been extended to optionally return the middle of the kth and the k+1 element.</p>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="k">module</span> <span class="m">Statistics</span> <span class="o">=</span>
<span class="c">/// Returns the median of three input values.</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs151', 474)" onmouseover="showTip(event, 'fs151', 474)" class="fn">median3</span> <span onmouseout="hideTip(event, 'fs152', 475)" onmouseover="showTip(event, 'fs152', 475)" class="id">a</span> <span onmouseout="hideTip(event, 'fs153', 476)" onmouseover="showTip(event, 'fs153', 476)" class="id">b</span> <span onmouseout="hideTip(event, 'fs154', 477)" onmouseover="showTip(event, 'fs154', 477)" class="id">c</span> <span class="o">=</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs152', 478)" onmouseover="showTip(event, 'fs152', 478)" class="id">a</span><span class="o"><=</span><span onmouseout="hideTip(event, 'fs153', 479)" onmouseover="showTip(event, 'fs153', 479)" class="id">b</span> <span class="k">then</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs153', 480)" onmouseover="showTip(event, 'fs153', 480)" class="id">b</span><span class="o"><=</span><span onmouseout="hideTip(event, 'fs154', 481)" onmouseover="showTip(event, 'fs154', 481)" class="id">c</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs153', 482)" onmouseover="showTip(event, 'fs153', 482)" class="id">b</span>
<span class="k">elif</span> <span onmouseout="hideTip(event, 'fs154', 483)" onmouseover="showTip(event, 'fs154', 483)" class="id">c</span><span class="o"><=</span><span onmouseout="hideTip(event, 'fs152', 484)" onmouseover="showTip(event, 'fs152', 484)" class="id">a</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs152', 485)" onmouseover="showTip(event, 'fs152', 485)" class="id">a</span>
<span class="k">else</span> <span onmouseout="hideTip(event, 'fs154', 486)" onmouseover="showTip(event, 'fs154', 486)" class="id">c</span>
<span class="k">else</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs152', 487)" onmouseover="showTip(event, 'fs152', 487)" class="id">a</span><span class="o"><=</span><span onmouseout="hideTip(event, 'fs154', 488)" onmouseover="showTip(event, 'fs154', 488)" class="id">c</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs152', 489)" onmouseover="showTip(event, 'fs152', 489)" class="id">a</span>
<span class="k">elif</span> <span onmouseout="hideTip(event, 'fs154', 490)" onmouseover="showTip(event, 'fs154', 490)" class="id">c</span><span class="o"><=</span><span onmouseout="hideTip(event, 'fs153', 491)" onmouseover="showTip(event, 'fs153', 491)" class="id">b</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs153', 492)" onmouseover="showTip(event, 'fs153', 492)" class="id">b</span>
<span class="k">else</span> <span onmouseout="hideTip(event, 'fs154', 493)" onmouseover="showTip(event, 'fs154', 493)" class="id">c</span>
<span class="c">/// Returns the minimum value of a subsection of an array.</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs155', 494)" onmouseover="showTip(event, 'fs155', 494)" class="fn">minSub</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs156', 495)" onmouseover="showTip(event, 'fs156', 495)" class="id">a</span><span class="pn">:</span><span class="id">_</span><span class="pn">[</span><span class="pn">]</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs157', 496)" onmouseover="showTip(event, 'fs157', 496)" class="id">lo</span> <span onmouseout="hideTip(event, 'fs158', 497)" onmouseover="showTip(event, 'fs158', 497)" class="id">hi</span> <span class="o">=</span>
<span class="k">let</span> <span class="k">mutable</span> <span onmouseout="hideTip(event, 'fs159', 498)" onmouseover="showTip(event, 'fs159', 498)" class="mv">v</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs156', 499)" onmouseover="showTip(event, 'fs156', 499)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs157', 500)" onmouseover="showTip(event, 'fs157', 500)" class="id">lo</span><span class="pn">]</span>
<span class="k">for</span> <span onmouseout="hideTip(event, 'fs119', 501)" onmouseover="showTip(event, 'fs119', 501)" class="id">i</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs157', 502)" onmouseover="showTip(event, 'fs157', 502)" class="id">lo</span><span class="o">+</span><span class="n">1</span> <span class="k">to</span> <span onmouseout="hideTip(event, 'fs158', 503)" onmouseover="showTip(event, 'fs158', 503)" class="id">hi</span> <span class="k">do</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs160', 504)" onmouseover="showTip(event, 'fs160', 504)" class="id">nv</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs156', 505)" onmouseover="showTip(event, 'fs156', 505)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs119', 506)" onmouseover="showTip(event, 'fs119', 506)" class="id">i</span><span class="pn">]</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs160', 507)" onmouseover="showTip(event, 'fs160', 507)" class="id">nv</span><span class="o"><</span><span onmouseout="hideTip(event, 'fs159', 508)" onmouseover="showTip(event, 'fs159', 508)" class="mv">v</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs159', 509)" onmouseover="showTip(event, 'fs159', 509)" class="mv">v</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs160', 510)" onmouseover="showTip(event, 'fs160', 510)" class="id">nv</span>
<span onmouseout="hideTip(event, 'fs159', 511)" onmouseover="showTip(event, 'fs159', 511)" class="mv">v</span>
<span class="c">/// Returns the maximum value of a subsection of an array.</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs161', 512)" onmouseover="showTip(event, 'fs161', 512)" class="fn">maxSub</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs156', 513)" onmouseover="showTip(event, 'fs156', 513)" class="id">a</span><span class="pn">:</span><span class="id">_</span><span class="pn">[</span><span class="pn">]</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs157', 514)" onmouseover="showTip(event, 'fs157', 514)" class="id">lo</span> <span onmouseout="hideTip(event, 'fs158', 515)" onmouseover="showTip(event, 'fs158', 515)" class="id">hi</span> <span class="o">=</span>
<span class="k">let</span> <span class="k">mutable</span> <span onmouseout="hideTip(event, 'fs159', 516)" onmouseover="showTip(event, 'fs159', 516)" class="mv">v</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs156', 517)" onmouseover="showTip(event, 'fs156', 517)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs157', 518)" onmouseover="showTip(event, 'fs157', 518)" class="id">lo</span><span class="pn">]</span>
<span class="k">for</span> <span onmouseout="hideTip(event, 'fs119', 519)" onmouseover="showTip(event, 'fs119', 519)" class="id">i</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs157', 520)" onmouseover="showTip(event, 'fs157', 520)" class="id">lo</span><span class="o">+</span><span class="n">1</span> <span class="k">to</span> <span onmouseout="hideTip(event, 'fs158', 521)" onmouseover="showTip(event, 'fs158', 521)" class="id">hi</span> <span class="k">do</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs160', 522)" onmouseover="showTip(event, 'fs160', 522)" class="id">nv</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs156', 523)" onmouseover="showTip(event, 'fs156', 523)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs119', 524)" onmouseover="showTip(event, 'fs119', 524)" class="id">i</span><span class="pn">]</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs160', 525)" onmouseover="showTip(event, 'fs160', 525)" class="id">nv</span><span class="o">></span><span onmouseout="hideTip(event, 'fs159', 526)" onmouseover="showTip(event, 'fs159', 526)" class="mv">v</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs159', 527)" onmouseover="showTip(event, 'fs159', 527)" class="mv">v</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs160', 528)" onmouseover="showTip(event, 'fs160', 528)" class="id">nv</span>
<span onmouseout="hideTip(event, 'fs159', 529)" onmouseover="showTip(event, 'fs159', 529)" class="mv">v</span>
<span class="c">/// Returns the middle point of the two smallest values of a subsection of an array.</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs162', 530)" onmouseover="showTip(event, 'fs162', 530)" class="fn">min2middleSub</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs163', 531)" onmouseover="showTip(event, 'fs163', 531)" class="id">a</span><span class="pn">:</span><span class="id">_</span><span class="pn">[</span><span class="pn">]</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs157', 532)" onmouseover="showTip(event, 'fs157', 532)" class="id">lo</span> <span onmouseout="hideTip(event, 'fs158', 533)" onmouseover="showTip(event, 'fs158', 533)" class="id">hi</span> <span class="o">=</span>
<span class="k">let</span> <span class="k">mutable</span> <span onmouseout="hideTip(event, 'fs164', 534)" onmouseover="showTip(event, 'fs164', 534)" class="mv">v0</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs163', 535)" onmouseover="showTip(event, 'fs163', 535)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs157', 536)" onmouseover="showTip(event, 'fs157', 536)" class="id">lo</span><span class="pn">]</span>
<span class="k">let</span> <span class="k">mutable</span> <span onmouseout="hideTip(event, 'fs165', 537)" onmouseover="showTip(event, 'fs165', 537)" class="mv">v1</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs163', 538)" onmouseover="showTip(event, 'fs163', 538)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs157', 539)" onmouseover="showTip(event, 'fs157', 539)" class="id">lo</span><span class="o">+</span><span class="n">1</span><span class="pn">]</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs165', 540)" onmouseover="showTip(event, 'fs165', 540)" class="mv">v1</span><span class="o"><</span><span onmouseout="hideTip(event, 'fs164', 541)" onmouseover="showTip(event, 'fs164', 541)" class="mv">v0</span> <span class="k">then</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs166', 542)" onmouseover="showTip(event, 'fs166', 542)" class="id">tmp</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs164', 543)" onmouseover="showTip(event, 'fs164', 543)" class="mv">v0</span>
<span onmouseout="hideTip(event, 'fs164', 544)" onmouseover="showTip(event, 'fs164', 544)" class="mv">v0</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs165', 545)" onmouseover="showTip(event, 'fs165', 545)" class="mv">v1</span>
<span onmouseout="hideTip(event, 'fs165', 546)" onmouseover="showTip(event, 'fs165', 546)" class="mv">v1</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs166', 547)" onmouseover="showTip(event, 'fs166', 547)" class="id">tmp</span>
<span class="k">for</span> <span onmouseout="hideTip(event, 'fs119', 548)" onmouseover="showTip(event, 'fs119', 548)" class="id">i</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs157', 549)" onmouseover="showTip(event, 'fs157', 549)" class="id">lo</span><span class="o">+</span><span class="n">2</span> <span class="k">to</span> <span onmouseout="hideTip(event, 'fs158', 550)" onmouseover="showTip(event, 'fs158', 550)" class="id">hi</span> <span class="k">do</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs167', 551)" onmouseover="showTip(event, 'fs167', 551)" class="id">nv</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs163', 552)" onmouseover="showTip(event, 'fs163', 552)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs119', 553)" onmouseover="showTip(event, 'fs119', 553)" class="id">i</span><span class="pn">]</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs167', 554)" onmouseover="showTip(event, 'fs167', 554)" class="id">nv</span><span class="o"><</span><span onmouseout="hideTip(event, 'fs164', 555)" onmouseover="showTip(event, 'fs164', 555)" class="mv">v0</span> <span class="k">then</span>
<span onmouseout="hideTip(event, 'fs165', 556)" onmouseover="showTip(event, 'fs165', 556)" class="mv">v1</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs164', 557)" onmouseover="showTip(event, 'fs164', 557)" class="mv">v0</span>
<span onmouseout="hideTip(event, 'fs164', 558)" onmouseover="showTip(event, 'fs164', 558)" class="mv">v0</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs167', 559)" onmouseover="showTip(event, 'fs167', 559)" class="id">nv</span>
<span class="k">elif</span> <span onmouseout="hideTip(event, 'fs167', 560)" onmouseover="showTip(event, 'fs167', 560)" class="id">nv</span><span class="o"><</span><span onmouseout="hideTip(event, 'fs165', 561)" onmouseover="showTip(event, 'fs165', 561)" class="mv">v1</span> <span class="k">then</span>
<span onmouseout="hideTip(event, 'fs165', 562)" onmouseover="showTip(event, 'fs165', 562)" class="mv">v1</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs167', 563)" onmouseover="showTip(event, 'fs167', 563)" class="id">nv</span>
<span onmouseout="hideTip(event, 'fs33', 564)" onmouseover="showTip(event, 'fs33', 564)" class="fn">middleOrdered</span> <span onmouseout="hideTip(event, 'fs164', 565)" onmouseover="showTip(event, 'fs164', 565)" class="mv">v0</span> <span onmouseout="hideTip(event, 'fs165', 566)" onmouseover="showTip(event, 'fs165', 566)" class="mv">v1</span>
<span class="c">/// Returns the middle point of the two largest values of a subsection of an array.</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs168', 567)" onmouseover="showTip(event, 'fs168', 567)" class="fn">max2middleSub</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs163', 568)" onmouseover="showTip(event, 'fs163', 568)" class="id">a</span><span class="pn">:</span><span class="id">_</span><span class="pn">[</span><span class="pn">]</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs157', 569)" onmouseover="showTip(event, 'fs157', 569)" class="id">lo</span> <span onmouseout="hideTip(event, 'fs158', 570)" onmouseover="showTip(event, 'fs158', 570)" class="id">hi</span> <span class="o">=</span>
<span class="k">let</span> <span class="k">mutable</span> <span onmouseout="hideTip(event, 'fs164', 571)" onmouseover="showTip(event, 'fs164', 571)" class="mv">v0</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs163', 572)" onmouseover="showTip(event, 'fs163', 572)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs157', 573)" onmouseover="showTip(event, 'fs157', 573)" class="id">lo</span><span class="pn">]</span>
<span class="k">let</span> <span class="k">mutable</span> <span onmouseout="hideTip(event, 'fs165', 574)" onmouseover="showTip(event, 'fs165', 574)" class="mv">v1</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs163', 575)" onmouseover="showTip(event, 'fs163', 575)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs157', 576)" onmouseover="showTip(event, 'fs157', 576)" class="id">lo</span><span class="o">+</span><span class="n">1</span><span class="pn">]</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs165', 577)" onmouseover="showTip(event, 'fs165', 577)" class="mv">v1</span><span class="o">></span><span onmouseout="hideTip(event, 'fs164', 578)" onmouseover="showTip(event, 'fs164', 578)" class="mv">v0</span> <span class="k">then</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs166', 579)" onmouseover="showTip(event, 'fs166', 579)" class="id">tmp</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs164', 580)" onmouseover="showTip(event, 'fs164', 580)" class="mv">v0</span>
<span onmouseout="hideTip(event, 'fs164', 581)" onmouseover="showTip(event, 'fs164', 581)" class="mv">v0</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs165', 582)" onmouseover="showTip(event, 'fs165', 582)" class="mv">v1</span>
<span onmouseout="hideTip(event, 'fs165', 583)" onmouseover="showTip(event, 'fs165', 583)" class="mv">v1</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs166', 584)" onmouseover="showTip(event, 'fs166', 584)" class="id">tmp</span>
<span class="k">for</span> <span onmouseout="hideTip(event, 'fs119', 585)" onmouseover="showTip(event, 'fs119', 585)" class="id">i</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs157', 586)" onmouseover="showTip(event, 'fs157', 586)" class="id">lo</span><span class="o">+</span><span class="n">2</span> <span class="k">to</span> <span onmouseout="hideTip(event, 'fs158', 587)" onmouseover="showTip(event, 'fs158', 587)" class="id">hi</span> <span class="k">do</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs167', 588)" onmouseover="showTip(event, 'fs167', 588)" class="id">nv</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs163', 589)" onmouseover="showTip(event, 'fs163', 589)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs119', 590)" onmouseover="showTip(event, 'fs119', 590)" class="id">i</span><span class="pn">]</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs167', 591)" onmouseover="showTip(event, 'fs167', 591)" class="id">nv</span><span class="o">></span><span onmouseout="hideTip(event, 'fs164', 592)" onmouseover="showTip(event, 'fs164', 592)" class="mv">v0</span> <span class="k">then</span>
<span onmouseout="hideTip(event, 'fs165', 593)" onmouseover="showTip(event, 'fs165', 593)" class="mv">v1</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs164', 594)" onmouseover="showTip(event, 'fs164', 594)" class="mv">v0</span>
<span onmouseout="hideTip(event, 'fs164', 595)" onmouseover="showTip(event, 'fs164', 595)" class="mv">v0</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs167', 596)" onmouseover="showTip(event, 'fs167', 596)" class="id">nv</span>
<span class="k">elif</span> <span onmouseout="hideTip(event, 'fs167', 597)" onmouseover="showTip(event, 'fs167', 597)" class="id">nv</span><span class="o">></span><span onmouseout="hideTip(event, 'fs165', 598)" onmouseover="showTip(event, 'fs165', 598)" class="mv">v1</span> <span class="k">then</span>
<span onmouseout="hideTip(event, 'fs165', 599)" onmouseover="showTip(event, 'fs165', 599)" class="mv">v1</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs167', 600)" onmouseover="showTip(event, 'fs167', 600)" class="id">nv</span>
<span onmouseout="hideTip(event, 'fs33', 601)" onmouseover="showTip(event, 'fs33', 601)" class="fn">middleOrdered</span> <span onmouseout="hideTip(event, 'fs165', 602)" onmouseover="showTip(event, 'fs165', 602)" class="mv">v1</span> <span onmouseout="hideTip(event, 'fs164', 603)" onmouseover="showTip(event, 'fs164', 603)" class="mv">v0</span>
<span class="c">/// Swap two elements in an array.</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs169', 604)" onmouseover="showTip(event, 'fs169', 604)" class="fn">swap</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs170', 605)" onmouseover="showTip(event, 'fs170', 605)" class="id">a</span><span class="pn">:</span><span class="id">_</span><span class="pn">[</span><span class="pn">]</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs119', 606)" onmouseover="showTip(event, 'fs119', 606)" class="id">i</span> <span onmouseout="hideTip(event, 'fs171', 607)" onmouseover="showTip(event, 'fs171', 607)" class="id">j</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs172', 608)" onmouseover="showTip(event, 'fs172', 608)" class="id">temp</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs170', 609)" onmouseover="showTip(event, 'fs170', 609)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs119', 610)" onmouseover="showTip(event, 'fs119', 610)" class="id">i</span><span class="pn">]</span>
<span onmouseout="hideTip(event, 'fs170', 611)" onmouseover="showTip(event, 'fs170', 611)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs119', 612)" onmouseover="showTip(event, 'fs119', 612)" class="id">i</span><span class="pn">]</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs170', 613)" onmouseover="showTip(event, 'fs170', 613)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs171', 614)" onmouseover="showTip(event, 'fs171', 614)" class="id">j</span><span class="pn">]</span>
<span onmouseout="hideTip(event, 'fs170', 615)" onmouseover="showTip(event, 'fs170', 615)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs171', 616)" onmouseover="showTip(event, 'fs171', 616)" class="id">j</span><span class="pn">]</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs172', 617)" onmouseover="showTip(event, 'fs172', 617)" class="id">temp</span>
<span class="c">/// Swap two elements in an array if the first is larger than the second.</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs173', 618)" onmouseover="showTip(event, 'fs173', 618)" class="fn">swapIf</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs156', 619)" onmouseover="showTip(event, 'fs156', 619)" class="id">a</span><span class="pn">:</span><span class="id">_</span><span class="pn">[</span><span class="pn">]</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs119', 620)" onmouseover="showTip(event, 'fs119', 620)" class="id">i</span> <span onmouseout="hideTip(event, 'fs171', 621)" onmouseover="showTip(event, 'fs171', 621)" class="id">j</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs174', 622)" onmouseover="showTip(event, 'fs174', 622)" class="id">ai</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs156', 623)" onmouseover="showTip(event, 'fs156', 623)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs119', 624)" onmouseover="showTip(event, 'fs119', 624)" class="id">i</span><span class="pn">]</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs175', 625)" onmouseover="showTip(event, 'fs175', 625)" class="id">aj</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs156', 626)" onmouseover="showTip(event, 'fs156', 626)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs171', 627)" onmouseover="showTip(event, 'fs171', 627)" class="id">j</span><span class="pn">]</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs174', 628)" onmouseover="showTip(event, 'fs174', 628)" class="id">ai</span><span class="o">></span><span onmouseout="hideTip(event, 'fs175', 629)" onmouseover="showTip(event, 'fs175', 629)" class="id">aj</span> <span class="k">then</span>
<span onmouseout="hideTip(event, 'fs156', 630)" onmouseover="showTip(event, 'fs156', 630)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs119', 631)" onmouseover="showTip(event, 'fs119', 631)" class="id">i</span><span class="pn">]</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs175', 632)" onmouseover="showTip(event, 'fs175', 632)" class="id">aj</span>
<span onmouseout="hideTip(event, 'fs156', 633)" onmouseover="showTip(event, 'fs156', 633)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs171', 634)" onmouseover="showTip(event, 'fs171', 634)" class="id">j</span><span class="pn">]</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs174', 635)" onmouseover="showTip(event, 'fs174', 635)" class="id">ai</span>
<span class="c">/// Returns the kth smallest element in an array and optionally the middle with</span>
<span class="c">/// the next largest. Elements will be reordered in place and cannot be equal</span>
<span class="c">/// to the max or min value of the generic type.</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs176', 636)" onmouseover="showTip(event, 'fs176', 636)" class="fn">selectInPlace</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs177', 637)" onmouseover="showTip(event, 'fs177', 637)" class="id">a</span><span class="pn">:</span><span class="id">_</span><span class="pn">[</span><span class="pn">]</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs178', 638)" onmouseover="showTip(event, 'fs178', 638)" class="id">k</span> <span onmouseout="hideTip(event, 'fs179', 639)" onmouseover="showTip(event, 'fs179', 639)" class="id">middleNext</span> <span class="o">=</span>
<span class="k">let</span> <span class="k">rec</span> <span onmouseout="hideTip(event, 'fs180', 640)" onmouseover="showTip(event, 'fs180', 640)" class="fn">outerLoop</span> <span onmouseout="hideTip(event, 'fs157', 641)" onmouseover="showTip(event, 'fs157', 641)" class="id">lo</span> <span onmouseout="hideTip(event, 'fs158', 642)" onmouseover="showTip(event, 'fs158', 642)" class="id">hi</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs173', 643)" onmouseover="showTip(event, 'fs173', 643)" class="fn">swapIf</span> <span onmouseout="hideTip(event, 'fs177', 644)" onmouseover="showTip(event, 'fs177', 644)" class="id">a</span> <span onmouseout="hideTip(event, 'fs157', 645)" onmouseover="showTip(event, 'fs157', 645)" class="id">lo</span> <span onmouseout="hideTip(event, 'fs178', 646)" onmouseover="showTip(event, 'fs178', 646)" class="id">k</span>
<span onmouseout="hideTip(event, 'fs173', 647)" onmouseover="showTip(event, 'fs173', 647)" class="fn">swapIf</span> <span onmouseout="hideTip(event, 'fs177', 648)" onmouseover="showTip(event, 'fs177', 648)" class="id">a</span> <span onmouseout="hideTip(event, 'fs157', 649)" onmouseover="showTip(event, 'fs157', 649)" class="id">lo</span> <span onmouseout="hideTip(event, 'fs158', 650)" onmouseover="showTip(event, 'fs158', 650)" class="id">hi</span>
<span onmouseout="hideTip(event, 'fs173', 651)" onmouseover="showTip(event, 'fs173', 651)" class="fn">swapIf</span> <span onmouseout="hideTip(event, 'fs177', 652)" onmouseover="showTip(event, 'fs177', 652)" class="id">a</span> <span onmouseout="hideTip(event, 'fs178', 653)" onmouseover="showTip(event, 'fs178', 653)" class="id">k</span> <span onmouseout="hideTip(event, 'fs158', 654)" onmouseover="showTip(event, 'fs158', 654)" class="id">hi</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs181', 655)" onmouseover="showTip(event, 'fs181', 655)" class="id">pivot</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs177', 656)" onmouseover="showTip(event, 'fs177', 656)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs178', 657)" onmouseover="showTip(event, 'fs178', 657)" class="id">k</span><span class="pn">]</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs182', 658)" onmouseover="showTip(event, 'fs182', 658)" class="id">resetLo</span> <span class="o">=</span> <span class="k">if</span> <span onmouseout="hideTip(event, 'fs177', 659)" onmouseover="showTip(event, 'fs177', 659)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs157', 660)" onmouseover="showTip(event, 'fs157', 660)" class="id">lo</span><span class="pn">]</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs181', 661)" onmouseover="showTip(event, 'fs181', 661)" class="id">pivot</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs177', 662)" onmouseover="showTip(event, 'fs177', 662)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs157', 663)" onmouseover="showTip(event, 'fs157', 663)" class="id">lo</span><span class="pn">]</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs19', 664)" onmouseover="showTip(event, 'fs19', 664)" class="fn">minValue</span><span class="pn">(</span><span class="pn">)</span><span class="pn">;</span> <span class="k">true</span> <span class="k">else</span> <span class="k">false</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs183', 665)" onmouseover="showTip(event, 'fs183', 665)" class="id">resetHi</span> <span class="o">=</span> <span class="k">if</span> <span onmouseout="hideTip(event, 'fs177', 666)" onmouseover="showTip(event, 'fs177', 666)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs158', 667)" onmouseover="showTip(event, 'fs158', 667)" class="id">hi</span><span class="pn">]</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs181', 668)" onmouseover="showTip(event, 'fs181', 668)" class="id">pivot</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs177', 669)" onmouseover="showTip(event, 'fs177', 669)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs158', 670)" onmouseover="showTip(event, 'fs158', 670)" class="id">hi</span><span class="pn">]</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs26', 671)" onmouseover="showTip(event, 'fs26', 671)" class="fn">maxValue</span><span class="pn">(</span><span class="pn">)</span><span class="pn">;</span> <span class="k">true</span> <span class="k">else</span> <span class="k">false</span>
<span class="k">let</span> <span class="k">mutable</span> <span onmouseout="hideTip(event, 'fs184', 672)" onmouseover="showTip(event, 'fs184', 672)" class="mv">i</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs157', 673)" onmouseover="showTip(event, 'fs157', 673)" class="id">lo</span><span class="o">+</span><span class="n">1</span>
<span class="k">let</span> <span class="k">mutable</span> <span onmouseout="hideTip(event, 'fs185', 674)" onmouseover="showTip(event, 'fs185', 674)" class="mv">j</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs158', 675)" onmouseover="showTip(event, 'fs158', 675)" class="id">hi</span><span class="o">-</span><span class="n">1</span>
<span class="k">while</span> <span onmouseout="hideTip(event, 'fs177', 676)" onmouseover="showTip(event, 'fs177', 676)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs184', 677)" onmouseover="showTip(event, 'fs184', 677)" class="mv">i</span><span class="pn">]</span><span class="o"><=</span><span onmouseout="hideTip(event, 'fs181', 678)" onmouseover="showTip(event, 'fs181', 678)" class="id">pivot</span> <span class="k">do</span> <span onmouseout="hideTip(event, 'fs184', 679)" onmouseover="showTip(event, 'fs184', 679)" class="mv">i</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs184', 680)" onmouseover="showTip(event, 'fs184', 680)" class="mv">i</span><span class="o">+</span><span class="n">1</span>
<span class="k">while</span> <span onmouseout="hideTip(event, 'fs177', 681)" onmouseover="showTip(event, 'fs177', 681)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs185', 682)" onmouseover="showTip(event, 'fs185', 682)" class="mv">j</span><span class="pn">]</span><span class="o">></span><span class="o">=</span><span onmouseout="hideTip(event, 'fs181', 683)" onmouseover="showTip(event, 'fs181', 683)" class="id">pivot</span> <span class="k">do</span> <span onmouseout="hideTip(event, 'fs185', 684)" onmouseover="showTip(event, 'fs185', 684)" class="mv">j</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs185', 685)" onmouseover="showTip(event, 'fs185', 685)" class="mv">j</span><span class="o">-</span><span class="n">1</span>
<span class="k">while</span> <span onmouseout="hideTip(event, 'fs184', 686)" onmouseover="showTip(event, 'fs184', 686)" class="mv">i</span><span class="o"><</span><span onmouseout="hideTip(event, 'fs178', 687)" onmouseover="showTip(event, 'fs178', 687)" class="id">k</span> <span class="o">&&</span> <span onmouseout="hideTip(event, 'fs178', 688)" onmouseover="showTip(event, 'fs178', 688)" class="id">k</span><span class="o"><</span><span onmouseout="hideTip(event, 'fs185', 689)" onmouseover="showTip(event, 'fs185', 689)" class="mv">j</span> <span class="k">do</span>
<span onmouseout="hideTip(event, 'fs169', 690)" onmouseover="showTip(event, 'fs169', 690)" class="fn">swap</span> <span onmouseout="hideTip(event, 'fs177', 691)" onmouseover="showTip(event, 'fs177', 691)" class="id">a</span> <span onmouseout="hideTip(event, 'fs184', 692)" onmouseover="showTip(event, 'fs184', 692)" class="mv">i</span> <span onmouseout="hideTip(event, 'fs185', 693)" onmouseover="showTip(event, 'fs185', 693)" class="mv">j</span>
<span onmouseout="hideTip(event, 'fs184', 694)" onmouseover="showTip(event, 'fs184', 694)" class="mv">i</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs184', 695)" onmouseover="showTip(event, 'fs184', 695)" class="mv">i</span><span class="o">+</span><span class="n">1</span>
<span onmouseout="hideTip(event, 'fs185', 696)" onmouseover="showTip(event, 'fs185', 696)" class="mv">j</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs185', 697)" onmouseover="showTip(event, 'fs185', 697)" class="mv">j</span><span class="o">-</span><span class="n">1</span>
<span class="k">while</span> <span onmouseout="hideTip(event, 'fs177', 698)" onmouseover="showTip(event, 'fs177', 698)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs184', 699)" onmouseover="showTip(event, 'fs184', 699)" class="mv">i</span><span class="pn">]</span><span class="o"><=</span><span onmouseout="hideTip(event, 'fs181', 700)" onmouseover="showTip(event, 'fs181', 700)" class="id">pivot</span> <span class="k">do</span> <span onmouseout="hideTip(event, 'fs184', 701)" onmouseover="showTip(event, 'fs184', 701)" class="mv">i</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs184', 702)" onmouseover="showTip(event, 'fs184', 702)" class="mv">i</span><span class="o">+</span><span class="n">1</span>
<span class="k">while</span> <span onmouseout="hideTip(event, 'fs177', 703)" onmouseover="showTip(event, 'fs177', 703)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs185', 704)" onmouseover="showTip(event, 'fs185', 704)" class="mv">j</span><span class="pn">]</span><span class="o">></span><span class="o">=</span><span onmouseout="hideTip(event, 'fs181', 705)" onmouseover="showTip(event, 'fs181', 705)" class="id">pivot</span> <span class="k">do</span> <span onmouseout="hideTip(event, 'fs185', 706)" onmouseover="showTip(event, 'fs185', 706)" class="mv">j</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs185', 707)" onmouseover="showTip(event, 'fs185', 707)" class="mv">j</span><span class="o">-</span><span class="n">1</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs184', 708)" onmouseover="showTip(event, 'fs184', 708)" class="mv">i</span><span class="o"><</span><span onmouseout="hideTip(event, 'fs185', 709)" onmouseover="showTip(event, 'fs185', 709)" class="mv">j</span> <span class="k">then</span>
<span onmouseout="hideTip(event, 'fs169', 710)" onmouseover="showTip(event, 'fs169', 710)" class="fn">swap</span> <span onmouseout="hideTip(event, 'fs177', 711)" onmouseover="showTip(event, 'fs177', 711)" class="id">a</span> <span onmouseout="hideTip(event, 'fs184', 712)" onmouseover="showTip(event, 'fs184', 712)" class="mv">i</span> <span onmouseout="hideTip(event, 'fs185', 713)" onmouseover="showTip(event, 'fs185', 713)" class="mv">j</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs178', 714)" onmouseover="showTip(event, 'fs178', 714)" class="id">k</span><span class="o"><</span><span onmouseout="hideTip(event, 'fs185', 715)" onmouseover="showTip(event, 'fs185', 715)" class="mv">j</span> <span class="k">then</span>
<span onmouseout="hideTip(event, 'fs184', 716)" onmouseover="showTip(event, 'fs184', 716)" class="mv">i</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs157', 717)" onmouseover="showTip(event, 'fs157', 717)" class="id">lo</span>
<span onmouseout="hideTip(event, 'fs185', 718)" onmouseover="showTip(event, 'fs185', 718)" class="mv">j</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs185', 719)" onmouseover="showTip(event, 'fs185', 719)" class="mv">j</span><span class="o">-</span><span class="n">1</span>
<span class="k">while</span> <span onmouseout="hideTip(event, 'fs177', 720)" onmouseover="showTip(event, 'fs177', 720)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs185', 721)" onmouseover="showTip(event, 'fs185', 721)" class="mv">j</span><span class="pn">]</span><span class="o">></span><span class="o">=</span><span onmouseout="hideTip(event, 'fs181', 722)" onmouseover="showTip(event, 'fs181', 722)" class="id">pivot</span> <span class="k">do</span> <span onmouseout="hideTip(event, 'fs185', 723)" onmouseover="showTip(event, 'fs185', 723)" class="mv">j</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs185', 724)" onmouseover="showTip(event, 'fs185', 724)" class="mv">j</span><span class="o">-</span><span class="n">1</span>
<span class="k">else</span>
<span onmouseout="hideTip(event, 'fs185', 725)" onmouseover="showTip(event, 'fs185', 725)" class="mv">j</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs158', 726)" onmouseover="showTip(event, 'fs158', 726)" class="id">hi</span>
<span onmouseout="hideTip(event, 'fs184', 727)" onmouseover="showTip(event, 'fs184', 727)" class="mv">i</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs184', 728)" onmouseover="showTip(event, 'fs184', 728)" class="mv">i</span><span class="o">+</span><span class="n">1</span>
<span class="k">while</span> <span onmouseout="hideTip(event, 'fs177', 729)" onmouseover="showTip(event, 'fs177', 729)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs184', 730)" onmouseover="showTip(event, 'fs184', 730)" class="mv">i</span><span class="pn">]</span><span class="o"><=</span><span onmouseout="hideTip(event, 'fs181', 731)" onmouseover="showTip(event, 'fs181', 731)" class="id">pivot</span> <span class="k">do</span> <span onmouseout="hideTip(event, 'fs184', 732)" onmouseover="showTip(event, 'fs184', 732)" class="mv">i</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs184', 733)" onmouseover="showTip(event, 'fs184', 733)" class="mv">i</span><span class="o">+</span><span class="n">1</span>
<span class="k">else</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs178', 734)" onmouseover="showTip(event, 'fs178', 734)" class="id">k</span><span class="o"><</span><span onmouseout="hideTip(event, 'fs185', 735)" onmouseover="showTip(event, 'fs185', 735)" class="mv">j</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs184', 736)" onmouseover="showTip(event, 'fs184', 736)" class="mv">i</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs157', 737)" onmouseover="showTip(event, 'fs157', 737)" class="id">lo</span>
<span class="k">elif</span> <span onmouseout="hideTip(event, 'fs178', 738)" onmouseover="showTip(event, 'fs178', 738)" class="id">k</span><span class="o">></span><span onmouseout="hideTip(event, 'fs184', 739)" onmouseover="showTip(event, 'fs184', 739)" class="mv">i</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs185', 740)" onmouseover="showTip(event, 'fs185', 740)" class="mv">j</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs158', 741)" onmouseover="showTip(event, 'fs158', 741)" class="id">hi</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs182', 742)" onmouseover="showTip(event, 'fs182', 742)" class="id">resetLo</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs177', 743)" onmouseover="showTip(event, 'fs177', 743)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs157', 744)" onmouseover="showTip(event, 'fs157', 744)" class="id">lo</span><span class="pn">]</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs181', 745)" onmouseover="showTip(event, 'fs181', 745)" class="id">pivot</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs183', 746)" onmouseover="showTip(event, 'fs183', 746)" class="id">resetHi</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs177', 747)" onmouseover="showTip(event, 'fs177', 747)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs158', 748)" onmouseover="showTip(event, 'fs158', 748)" class="id">hi</span><span class="pn">]</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs181', 749)" onmouseover="showTip(event, 'fs181', 749)" class="id">pivot</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs184', 750)" onmouseover="showTip(event, 'fs184', 750)" class="mv">i</span><span class="o">></span><span class="o">=</span><span onmouseout="hideTip(event, 'fs185', 751)" onmouseover="showTip(event, 'fs185', 751)" class="mv">j</span> <span class="k">then</span> <span class="k">if</span> <span onmouseout="hideTip(event, 'fs179', 752)" onmouseover="showTip(event, 'fs179', 752)" class="id">middleNext</span> <span class="k">then</span> <span class="k">if</span> <span onmouseout="hideTip(event, 'fs178', 753)" onmouseover="showTip(event, 'fs178', 753)" class="id">k</span><span class="o">+</span><span class="n">1</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs184', 754)" onmouseover="showTip(event, 'fs184', 754)" class="mv">i</span> <span class="k">then</span>
<span onmouseout="hideTip(event, 'fs155', 755)" onmouseover="showTip(event, 'fs155', 755)" class="fn">minSub</span> <span onmouseout="hideTip(event, 'fs177', 756)" onmouseover="showTip(event, 'fs177', 756)" class="id">a</span> <span onmouseout="hideTip(event, 'fs184', 757)" onmouseover="showTip(event, 'fs184', 757)" class="mv">i</span> <span onmouseout="hideTip(event, 'fs158', 758)" onmouseover="showTip(event, 'fs158', 758)" class="id">hi</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs33', 759)" onmouseover="showTip(event, 'fs33', 759)" class="fn">middleOrdered</span> <span onmouseout="hideTip(event, 'fs181', 760)" onmouseover="showTip(event, 'fs181', 760)" class="id">pivot</span>
<span class="k">else</span> <span onmouseout="hideTip(event, 'fs181', 761)" onmouseover="showTip(event, 'fs181', 761)" class="id">pivot</span>
<span class="k">else</span> <span onmouseout="hideTip(event, 'fs181', 762)" onmouseover="showTip(event, 'fs181', 762)" class="id">pivot</span>
<span class="k">elif</span> <span onmouseout="hideTip(event, 'fs178', 763)" onmouseover="showTip(event, 'fs178', 763)" class="id">k</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs184', 764)" onmouseover="showTip(event, 'fs184', 764)" class="mv">i</span> <span class="k">then</span> <span class="k">if</span> <span onmouseout="hideTip(event, 'fs179', 765)" onmouseover="showTip(event, 'fs179', 765)" class="id">middleNext</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs162', 766)" onmouseover="showTip(event, 'fs162', 766)" class="fn">min2middleSub</span> <span onmouseout="hideTip(event, 'fs177', 767)" onmouseover="showTip(event, 'fs177', 767)" class="id">a</span> <span onmouseout="hideTip(event, 'fs184', 768)" onmouseover="showTip(event, 'fs184', 768)" class="mv">i</span> <span onmouseout="hideTip(event, 'fs185', 769)" onmouseover="showTip(event, 'fs185', 769)" class="mv">j</span> <span class="k">else</span> <span onmouseout="hideTip(event, 'fs155', 770)" onmouseover="showTip(event, 'fs155', 770)" class="fn">minSub</span> <span onmouseout="hideTip(event, 'fs177', 771)" onmouseover="showTip(event, 'fs177', 771)" class="id">a</span> <span onmouseout="hideTip(event, 'fs184', 772)" onmouseover="showTip(event, 'fs184', 772)" class="mv">i</span> <span onmouseout="hideTip(event, 'fs185', 773)" onmouseover="showTip(event, 'fs185', 773)" class="mv">j</span>
<span class="k">elif</span> <span onmouseout="hideTip(event, 'fs178', 774)" onmouseover="showTip(event, 'fs178', 774)" class="id">k</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs185', 775)" onmouseover="showTip(event, 'fs185', 775)" class="mv">j</span> <span class="k">then</span> <span class="k">if</span> <span onmouseout="hideTip(event, 'fs179', 776)" onmouseover="showTip(event, 'fs179', 776)" class="id">middleNext</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs168', 777)" onmouseover="showTip(event, 'fs168', 777)" class="fn">max2middleSub</span> <span onmouseout="hideTip(event, 'fs177', 778)" onmouseover="showTip(event, 'fs177', 778)" class="id">a</span> <span onmouseout="hideTip(event, 'fs184', 779)" onmouseover="showTip(event, 'fs184', 779)" class="mv">i</span> <span onmouseout="hideTip(event, 'fs185', 780)" onmouseover="showTip(event, 'fs185', 780)" class="mv">j</span> <span class="k">else</span> <span onmouseout="hideTip(event, 'fs161', 781)" onmouseover="showTip(event, 'fs161', 781)" class="fn">maxSub</span> <span onmouseout="hideTip(event, 'fs177', 782)" onmouseover="showTip(event, 'fs177', 782)" class="id">a</span> <span onmouseout="hideTip(event, 'fs184', 783)" onmouseover="showTip(event, 'fs184', 783)" class="mv">i</span> <span onmouseout="hideTip(event, 'fs185', 784)" onmouseover="showTip(event, 'fs185', 784)" class="mv">j</span>
<span class="k">else</span> <span onmouseout="hideTip(event, 'fs180', 785)" onmouseover="showTip(event, 'fs180', 785)" class="fn">outerLoop</span> <span onmouseout="hideTip(event, 'fs184', 786)" onmouseover="showTip(event, 'fs184', 786)" class="mv">i</span> <span onmouseout="hideTip(event, 'fs185', 787)" onmouseover="showTip(event, 'fs185', 787)" class="mv">j</span>
<span onmouseout="hideTip(event, 'fs180', 788)" onmouseover="showTip(event, 'fs180', 788)" class="fn">outerLoop</span> <span class="n">0</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs71', 789)" onmouseover="showTip(event, 'fs71', 789)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs74', 790)" onmouseover="showTip(event, 'fs74', 790)" class="id">length</span> <span onmouseout="hideTip(event, 'fs177', 791)" onmouseover="showTip(event, 'fs177', 791)" class="id">a</span><span class="o">-</span><span class="n">1</span><span class="pn">)</span>
<span class="c">/// Returns the median of an array. Elements will be reordered in place and</span>
<span class="c">/// cannot be equal to the max or min value of the generic type.</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs186', 792)" onmouseover="showTip(event, 'fs186', 792)" class="fn">medianInPlace</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs187', 793)" onmouseover="showTip(event, 'fs187', 793)" class="id">a</span><span class="pn">:</span><span class="id">_</span><span class="pn">[</span><span class="pn">]</span><span class="pn">)</span> <span class="o">=</span>
<span class="k">match</span> <span onmouseout="hideTip(event, 'fs71', 794)" onmouseover="showTip(event, 'fs71', 794)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs74', 795)" onmouseover="showTip(event, 'fs74', 795)" class="id">length</span> <span onmouseout="hideTip(event, 'fs187', 796)" onmouseover="showTip(event, 'fs187', 796)" class="id">a</span><span class="o">-</span><span class="n">1</span> <span class="k">with</span>
<span class="pn">|</span> <span class="n">0</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs187', 797)" onmouseover="showTip(event, 'fs187', 797)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span class="n">0</span><span class="pn">]</span>
<span class="pn">|</span> <span class="n">1</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs36', 798)" onmouseover="showTip(event, 'fs36', 798)" class="fn">middle</span> <span onmouseout="hideTip(event, 'fs187', 799)" onmouseover="showTip(event, 'fs187', 799)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span class="n">0</span><span class="pn">]</span> <span onmouseout="hideTip(event, 'fs187', 800)" onmouseover="showTip(event, 'fs187', 800)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span class="n">1</span><span class="pn">]</span>
<span class="pn">|</span> <span class="n">2</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs151', 801)" onmouseover="showTip(event, 'fs151', 801)" class="fn">median3</span> <span onmouseout="hideTip(event, 'fs187', 802)" onmouseover="showTip(event, 'fs187', 802)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span class="n">0</span><span class="pn">]</span> <span onmouseout="hideTip(event, 'fs187', 803)" onmouseover="showTip(event, 'fs187', 803)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span class="n">1</span><span class="pn">]</span> <span onmouseout="hideTip(event, 'fs187', 804)" onmouseover="showTip(event, 'fs187', 804)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span class="n">2</span><span class="pn">]</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs188', 805)" onmouseover="showTip(event, 'fs188', 805)" class="id">last</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs176', 806)" onmouseover="showTip(event, 'fs176', 806)" class="fn">selectInPlace</span> <span onmouseout="hideTip(event, 'fs187', 807)" onmouseover="showTip(event, 'fs187', 807)" class="id">a</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs188', 808)" onmouseover="showTip(event, 'fs188', 808)" class="id">last</span><span class="o">/</span><span class="n">2</span><span class="pn">)</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs188', 809)" onmouseover="showTip(event, 'fs188', 809)" class="id">last</span><span class="o">%</span><span class="n">2</span><span class="o">=</span><span class="n">1</span><span class="pn">)</span>
<span class="c">/// Returns the median and median absolute deviation of an array.</span>
<span class="c">/// Elements cannot be equal to the max or min value of the generic type.</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs189', 810)" onmouseover="showTip(event, 'fs189', 810)" class="fn">medianAndMAD</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs190', 811)" onmouseover="showTip(event, 'fs190', 811)" class="id">a</span><span class="pn">:</span><span class="id">_</span><span class="pn">[</span><span class="pn">]</span><span class="pn">)</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs190', 812)" onmouseover="showTip(event, 'fs190', 812)" class="id">a</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs71', 813)" onmouseover="showTip(event, 'fs71', 813)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs191', 814)" onmouseover="showTip(event, 'fs191', 814)" class="id">copy</span> <span onmouseout="hideTip(event, 'fs190', 815)" onmouseover="showTip(event, 'fs190', 815)" class="id">a</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs192', 816)" onmouseover="showTip(event, 'fs192', 816)" class="id">median</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs186', 817)" onmouseover="showTip(event, 'fs186', 817)" class="fn">medianInPlace</span> <span onmouseout="hideTip(event, 'fs190', 818)" onmouseover="showTip(event, 'fs190', 818)" class="id">a</span>
<span class="k">for</span> <span onmouseout="hideTip(event, 'fs119', 819)" onmouseover="showTip(event, 'fs119', 819)" class="id">i</span> <span class="o">=</span> <span class="n">0</span> <span class="k">to</span> <span onmouseout="hideTip(event, 'fs71', 820)" onmouseover="showTip(event, 'fs71', 820)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs74', 821)" onmouseover="showTip(event, 'fs74', 821)" class="id">length</span> <span onmouseout="hideTip(event, 'fs190', 822)" onmouseover="showTip(event, 'fs190', 822)" class="id">a</span><span class="o">-</span><span class="n">1</span> <span class="k">do</span>
<span onmouseout="hideTip(event, 'fs190', 823)" onmouseover="showTip(event, 'fs190', 823)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs119', 824)" onmouseover="showTip(event, 'fs119', 824)" class="id">i</span><span class="pn">]</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs70', 825)" onmouseover="showTip(event, 'fs70', 825)" class="fn">abs</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs190', 826)" onmouseover="showTip(event, 'fs190', 826)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs119', 827)" onmouseover="showTip(event, 'fs119', 827)" class="id">i</span><span class="pn">]</span><span class="o">-</span><span onmouseout="hideTip(event, 'fs192', 828)" onmouseover="showTip(event, 'fs192', 828)" class="id">median</span><span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs192', 829)" onmouseover="showTip(event, 'fs192', 829)" class="id">median</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs186', 830)" onmouseover="showTip(event, 'fs186', 830)" class="fn">medianInPlace</span> <span onmouseout="hideTip(event, 'fs190', 831)" onmouseover="showTip(event, 'fs190', 831)" class="id">a</span>
</code></pre>
<h2><a name="Property-and-Performance-testing" class="anchor" href="#Property-and-Performance-testing">Property and Performance testing</a></h2>
<p>A simple <code>FsCheck</code> property test comparing the result with a full sort version ensures no mistakes have been made in the implementation.</p>
<p>The performance versus a full sort algorithm and the Math.Net C# Quickselect implementation are compared for different degrees of duplication and sorting.</p>
<p>The performance testing library developed in a previous <a href="/blog/2016/05/20/performance-testing">post</a> is used after extending it to allow subfunction measurement.
This is run from the build script in 64-bit Release mode.</p>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="k">module</span> <span onmouseout="hideTip(event, 'fs193', 832)" onmouseover="showTip(event, 'fs193', 832)" class="m">StatisticsTests</span> <span class="o">=</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs194', 833)" onmouseover="showTip(event, 'fs194', 833)" class="fn">medianQuickSelect</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs195', 834)" onmouseover="showTip(event, 'fs195', 834)" class="id">a</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs15', 835)" onmouseover="showTip(event, 'fs15', 835)" class="vt">float</span><span class="pn">[</span><span class="pn">]</span><span class="pn">)</span> <span class="o">=</span>
<span class="id">MathNet</span><span class="pn">.</span><span class="id">Numerics</span><span class="pn">.</span><span class="id">Statistics</span><span class="pn">.</span><span class="id">ArrayStatistics</span><span class="pn">.</span><span class="id">MedianInplace</span> <span onmouseout="hideTip(event, 'fs195', 836)" onmouseover="showTip(event, 'fs195', 836)" class="id">a</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs196', 837)" onmouseover="showTip(event, 'fs196', 837)" class="fn">medianFullSort</span> <span onmouseout="hideTip(event, 'fs195', 838)" onmouseover="showTip(event, 'fs195', 838)" class="id">a</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs71', 839)" onmouseover="showTip(event, 'fs71', 839)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs197', 840)" onmouseover="showTip(event, 'fs197', 840)" class="id">sortInPlace</span> <span onmouseout="hideTip(event, 'fs195', 841)" onmouseover="showTip(event, 'fs195', 841)" class="id">a</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs198', 842)" onmouseover="showTip(event, 'fs198', 842)" class="id">l</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs71', 843)" onmouseover="showTip(event, 'fs71', 843)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs74', 844)" onmouseover="showTip(event, 'fs74', 844)" class="id">length</span> <span onmouseout="hideTip(event, 'fs195', 845)" onmouseover="showTip(event, 'fs195', 845)" class="id">a</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs198', 846)" onmouseover="showTip(event, 'fs198', 846)" class="id">l</span><span class="o">%</span><span class="n">2</span><span class="o">=</span><span class="n">0</span> <span class="k">then</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs119', 847)" onmouseover="showTip(event, 'fs119', 847)" class="id">i</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs198', 848)" onmouseover="showTip(event, 'fs198', 848)" class="id">l</span><span class="o">/</span><span class="n">2</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs30', 849)" onmouseover="showTip(event, 'fs30', 849)" class="id">x</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs195', 850)" onmouseover="showTip(event, 'fs195', 850)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs119', 851)" onmouseover="showTip(event, 'fs119', 851)" class="id">i</span><span class="o">-</span><span class="n">1</span><span class="pn">]</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs199', 852)" onmouseover="showTip(event, 'fs199', 852)" class="id">y</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs195', 853)" onmouseover="showTip(event, 'fs195', 853)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs119', 854)" onmouseover="showTip(event, 'fs119', 854)" class="id">i</span><span class="pn">]</span>
<span onmouseout="hideTip(event, 'fs30', 855)" onmouseover="showTip(event, 'fs30', 855)" class="id">x</span><span class="o">+</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs199', 856)" onmouseover="showTip(event, 'fs199', 856)" class="id">y</span><span class="o">-</span><span onmouseout="hideTip(event, 'fs30', 857)" onmouseover="showTip(event, 'fs30', 857)" class="id">x</span><span class="pn">)</span><span class="o">*</span><span class="n">0.5</span>
<span class="k">else</span> <span onmouseout="hideTip(event, 'fs195', 858)" onmouseover="showTip(event, 'fs195', 858)" class="id">a</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs198', 859)" onmouseover="showTip(event, 'fs198', 859)" class="id">l</span><span class="o">/</span><span class="n">2</span><span class="pn">]</span>
<span class="pn">[<</span><span onmouseout="hideTip(event, 'fs3', 860)" onmouseover="showTip(event, 'fs3', 860)" class="rt">Property</span><span class="pn">>]</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs200', 861)" onmouseover="showTip(event, 'fs200', 861)" class="fn">MedianProp</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs28', 862)" onmouseover="showTip(event, 'fs28', 862)" class="id">x</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs9', 863)" onmouseover="showTip(event, 'fs9', 863)" class="vt">int</span><span class="pn">)</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs201', 864)" onmouseover="showTip(event, 'fs201', 864)" class="id">xs</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs9', 865)" onmouseover="showTip(event, 'fs9', 865)" class="vt">int</span> <span onmouseout="hideTip(event, 'fs202', 866)" onmouseover="showTip(event, 'fs202', 866)" class="rt">list</span><span class="pn">)</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs203', 867)" onmouseover="showTip(event, 'fs203', 867)" class="id">l</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs28', 868)" onmouseover="showTip(event, 'fs28', 868)" class="id">x</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs201', 869)" onmouseover="showTip(event, 'fs201', 869)" class="id">xs</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs204', 870)" onmouseover="showTip(event, 'fs204', 870)" class="m">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs205', 871)" onmouseover="showTip(event, 'fs205', 871)" class="id">map</span> <span onmouseout="hideTip(event, 'fs15', 872)" onmouseover="showTip(event, 'fs15', 872)" class="fn">float</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs206', 873)" onmouseover="showTip(event, 'fs206', 873)" class="id">m1</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs204', 874)" onmouseover="showTip(event, 'fs204', 874)" class="m">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs207', 875)" onmouseover="showTip(event, 'fs207', 875)" class="id">toArray</span> <span onmouseout="hideTip(event, 'fs203', 876)" onmouseover="showTip(event, 'fs203', 876)" class="id">l</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs208', 877)" onmouseover="showTip(event, 'fs208', 877)" class="m">Statistics</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs186', 878)" onmouseover="showTip(event, 'fs186', 878)" class="id">medianInPlace</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs209', 879)" onmouseover="showTip(event, 'fs209', 879)" class="id">m2</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs204', 880)" onmouseover="showTip(event, 'fs204', 880)" class="m">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs207', 881)" onmouseover="showTip(event, 'fs207', 881)" class="id">toArray</span> <span onmouseout="hideTip(event, 'fs203', 882)" onmouseover="showTip(event, 'fs203', 882)" class="id">l</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs196', 883)" onmouseover="showTip(event, 'fs196', 883)" class="fn">medianFullSort</span>
<span onmouseout="hideTip(event, 'fs206', 884)" onmouseover="showTip(event, 'fs206', 884)" class="id">m1</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs209', 885)" onmouseover="showTip(event, 'fs209', 885)" class="id">m2</span>
<span class="k">type</span> <span class="rt">Duplication</span> <span class="o">=</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs210', 886)" onmouseover="showTip(event, 'fs210', 886)" class="uc">Low</span> <span class="pn">|</span> <span onmouseout="hideTip(event, 'fs211', 887)" onmouseover="showTip(event, 'fs211', 887)" class="uc">Medium</span> <span class="pn">|</span> <span onmouseout="hideTip(event, 'fs212', 888)" onmouseover="showTip(event, 'fs212', 888)" class="uc">High</span>
<span class="k">member</span> <span onmouseout="hideTip(event, 'fs213', 889)" onmouseover="showTip(event, 'fs213', 889)" class="id">i</span><span class="pn">.</span><span class="prop">ToInt</span> <span class="o">=</span> <span class="k">match</span> <span onmouseout="hideTip(event, 'fs213', 890)" onmouseover="showTip(event, 'fs213', 890)" class="id">i</span> <span class="k">with</span> <span class="pn">|</span> <span onmouseout="hideTip(event, 'fs210', 891)" onmouseover="showTip(event, 'fs210', 891)" class="uc">Low</span><span class="k">-></span><span class="n">500000000</span> <span class="pn">|</span> <span onmouseout="hideTip(event, 'fs211', 892)" onmouseover="showTip(event, 'fs211', 892)" class="uc">Medium</span><span class="k">-></span><span class="n">5000</span> <span class="pn">|</span> <span onmouseout="hideTip(event, 'fs212', 893)" onmouseover="showTip(event, 'fs212', 893)" class="uc">High</span><span class="k">-></span><span class="n">50</span>
<span class="k">override</span> <span onmouseout="hideTip(event, 'fs213', 894)" onmouseover="showTip(event, 'fs213', 894)" class="id">i</span><span class="pn">.</span><span class="fn">ToString</span><span class="pn">(</span><span class="pn">)</span> <span class="o">=</span>
<span class="k">match</span> <span onmouseout="hideTip(event, 'fs213', 895)" onmouseover="showTip(event, 'fs213', 895)" class="id">i</span> <span class="k">with</span> <span class="pn">|</span> <span onmouseout="hideTip(event, 'fs210', 896)" onmouseover="showTip(event, 'fs210', 896)" class="uc">Low</span><span class="k">-></span><span class="s">"Low"</span> <span class="pn">|</span> <span onmouseout="hideTip(event, 'fs211', 897)" onmouseover="showTip(event, 'fs211', 897)" class="uc">Medium</span><span class="k">-></span><span class="s">"Medium"</span> <span class="pn">|</span> <span onmouseout="hideTip(event, 'fs212', 898)" onmouseover="showTip(event, 'fs212', 898)" class="uc">High</span><span class="k">-></span><span class="s">"High"</span>
<span class="k">type</span> <span class="rt">Sorted</span> <span class="o">=</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs214', 899)" onmouseover="showTip(event, 'fs214', 899)" class="uc">No</span> <span class="pn">|</span> <span onmouseout="hideTip(event, 'fs215', 900)" onmouseover="showTip(event, 'fs215', 900)" class="uc">Part</span> <span class="pn">|</span> <span onmouseout="hideTip(event, 'fs216', 901)" onmouseover="showTip(event, 'fs216', 901)" class="uc">Yes</span>
<span class="k">override</span> <span onmouseout="hideTip(event, 'fs217', 902)" onmouseover="showTip(event, 'fs217', 902)" class="id">i</span><span class="pn">.</span><span class="fn">ToString</span><span class="pn">(</span><span class="pn">)</span> <span class="o">=</span> <span class="k">match</span> <span onmouseout="hideTip(event, 'fs217', 903)" onmouseover="showTip(event, 'fs217', 903)" class="id">i</span> <span class="k">with</span> <span class="pn">|</span> <span onmouseout="hideTip(event, 'fs214', 904)" onmouseover="showTip(event, 'fs214', 904)" class="uc">No</span><span class="k">-></span><span class="s">"No"</span> <span class="pn">|</span> <span onmouseout="hideTip(event, 'fs215', 905)" onmouseover="showTip(event, 'fs215', 905)" class="uc">Part</span><span class="k">-></span><span class="s">"Part"</span> <span class="pn">|</span> <span onmouseout="hideTip(event, 'fs216', 906)" onmouseover="showTip(event, 'fs216', 906)" class="uc">Yes</span><span class="k">-></span><span class="s">"Yes"</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs218', 907)" onmouseover="showTip(event, 'fs218', 907)" class="fn">list</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs219', 908)" onmouseover="showTip(event, 'fs219', 908)" class="id">duplication</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs220', 909)" onmouseover="showTip(event, 'fs220', 909)" class="rt">Duplication</span><span class="pn">)</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs221', 910)" onmouseover="showTip(event, 'fs221', 910)" class="id">sorted</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs222', 911)" onmouseover="showTip(event, 'fs222', 911)" class="rt">Sorted</span><span class="pn">)</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs223', 912)" onmouseover="showTip(event, 'fs223', 912)" class="id">r</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs1', 913)" onmouseover="showTip(event, 'fs1', 913)" class="rt">System</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs224', 914)" onmouseover="showTip(event, 'fs224', 914)" class="id">Random</span> <span class="n">123</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs225', 915)" onmouseover="showTip(event, 'fs225', 915)" class="fn">next</span><span class="pn">(</span><span class="pn">)</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs223', 916)" onmouseover="showTip(event, 'fs223', 916)" class="fn">r</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs226', 917)" onmouseover="showTip(event, 'fs226', 917)" class="id">Next</span><span class="pn">(</span><span class="n">0</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs219', 918)" onmouseover="showTip(event, 'fs219', 918)" class="id">duplication</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs227', 919)" onmouseover="showTip(event, 'fs227', 919)" class="id">ToInt</span><span class="pn">)</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs15', 920)" onmouseover="showTip(event, 'fs15', 920)" class="fn">float</span>
<span onmouseout="hideTip(event, 'fs54', 921)" onmouseover="showTip(event, 'fs54', 921)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs228', 922)" onmouseover="showTip(event, 'fs228', 922)" class="id">init</span> <span class="n">5000</span> <span class="pn">(</span><span class="k">fun</span> <span onmouseout="hideTip(event, 'fs119', 923)" onmouseover="showTip(event, 'fs119', 923)" class="id">i</span> <span class="k">-></span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs203', 924)" onmouseover="showTip(event, 'fs203', 924)" class="id">l</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs204', 925)" onmouseover="showTip(event, 'fs204', 925)" class="m">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs229', 926)" onmouseover="showTip(event, 'fs229', 926)" class="id">init</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs119', 927)" onmouseover="showTip(event, 'fs119', 927)" class="id">i</span><span class="o">+</span><span class="n">1</span><span class="pn">)</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">_</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs225', 928)" onmouseover="showTip(event, 'fs225', 928)" class="fn">next</span><span class="pn">(</span><span class="pn">)</span><span class="pn">)</span>
<span class="k">match</span> <span onmouseout="hideTip(event, 'fs221', 929)" onmouseover="showTip(event, 'fs221', 929)" class="id">sorted</span> <span class="k">with</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs214', 930)" onmouseover="showTip(event, 'fs214', 930)" class="uc">No</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs203', 931)" onmouseover="showTip(event, 'fs203', 931)" class="id">l</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs216', 932)" onmouseover="showTip(event, 'fs216', 932)" class="uc">Yes</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs204', 933)" onmouseover="showTip(event, 'fs204', 933)" class="m">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs230', 934)" onmouseover="showTip(event, 'fs230', 934)" class="id">sort</span> <span onmouseout="hideTip(event, 'fs203', 935)" onmouseover="showTip(event, 'fs203', 935)" class="id">l</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs215', 936)" onmouseover="showTip(event, 'fs215', 936)" class="uc">Part</span> <span class="k">-></span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs195', 937)" onmouseover="showTip(event, 'fs195', 937)" class="id">a</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs204', 938)" onmouseover="showTip(event, 'fs204', 938)" class="m">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs230', 939)" onmouseover="showTip(event, 'fs230', 939)" class="id">sort</span> <span onmouseout="hideTip(event, 'fs203', 940)" onmouseover="showTip(event, 'fs203', 940)" class="id">l</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs204', 941)" onmouseover="showTip(event, 'fs204', 941)" class="m">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs207', 942)" onmouseover="showTip(event, 'fs207', 942)" class="id">toArray</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs231', 943)" onmouseover="showTip(event, 'fs231', 943)" class="id">len</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs71', 944)" onmouseover="showTip(event, 'fs71', 944)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs74', 945)" onmouseover="showTip(event, 'fs74', 945)" class="id">length</span> <span onmouseout="hideTip(event, 'fs195', 946)" onmouseover="showTip(event, 'fs195', 946)" class="id">a</span>
<span onmouseout="hideTip(event, 'fs54', 947)" onmouseover="showTip(event, 'fs54', 947)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs232', 948)" onmouseover="showTip(event, 'fs232', 948)" class="id">iter</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">_</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs208', 949)" onmouseover="showTip(event, 'fs208', 949)" class="m">Statistics</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs169', 950)" onmouseover="showTip(event, 'fs169', 950)" class="id">swap</span> <span onmouseout="hideTip(event, 'fs195', 951)" onmouseover="showTip(event, 'fs195', 951)" class="id">a</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs223', 952)" onmouseover="showTip(event, 'fs223', 952)" class="fn">r</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs226', 953)" onmouseover="showTip(event, 'fs226', 953)" class="id">Next</span> <span onmouseout="hideTip(event, 'fs231', 954)" onmouseover="showTip(event, 'fs231', 954)" class="id">len</span><span class="pn">)</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs223', 955)" onmouseover="showTip(event, 'fs223', 955)" class="fn">r</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs226', 956)" onmouseover="showTip(event, 'fs226', 956)" class="id">Next</span> <span onmouseout="hideTip(event, 'fs231', 957)" onmouseover="showTip(event, 'fs231', 957)" class="id">len</span><span class="pn">)</span><span class="pn">)</span>
<span class="pn">{</span><span class="n">1..</span><span onmouseout="hideTip(event, 'fs231', 958)" onmouseover="showTip(event, 'fs231', 958)" class="id">len</span><span class="o">/</span><span class="n">4</span><span class="pn">}</span>
<span onmouseout="hideTip(event, 'fs204', 959)" onmouseover="showTip(event, 'fs204', 959)" class="m">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs233', 960)" onmouseover="showTip(event, 'fs233', 960)" class="id">ofArray</span> <span onmouseout="hideTip(event, 'fs195', 961)" onmouseover="showTip(event, 'fs195', 961)" class="id">a</span>
<span class="pn">)</span>
<span class="pn">[<</span><span class="id">Fact</span><span class="pn">>]</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs234', 962)" onmouseover="showTip(event, 'fs234', 962)" class="fn">MedianPerfTest</span><span class="pn">(</span><span class="pn">)</span> <span class="o">=</span>
<span class="s">"| Duplication | Sorted | Current | MathNet | FullSort | 1.000 = |"</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs235', 963)" onmouseover="showTip(event, 'fs235', 963)" class="fn">printfn</span>
<span class="s">"|:-----------:|:----------:|:---------:|:---------:|:----------:|:---------:|"</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs235', 964)" onmouseover="showTip(event, 'fs235', 964)" class="fn">printfn</span>
<span onmouseout="hideTip(event, 'fs54', 965)" onmouseover="showTip(event, 'fs54', 965)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs236', 966)" onmouseover="showTip(event, 'fs236', 966)" class="id">collect</span> <span class="pn">(</span><span class="k">fun</span> <span onmouseout="hideTip(event, 'fs237', 967)" onmouseover="showTip(event, 'fs237', 967)" class="id">d</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs54', 968)" onmouseover="showTip(event, 'fs54', 968)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs55', 969)" onmouseover="showTip(event, 'fs55', 969)" class="id">map</span> <span class="pn">(</span><span class="k">fun</span> <span onmouseout="hideTip(event, 'fs238', 970)" onmouseover="showTip(event, 'fs238', 970)" class="id">s</span> <span class="k">-></span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs237', 971)" onmouseover="showTip(event, 'fs237', 971)" class="id">d</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs238', 972)" onmouseover="showTip(event, 'fs238', 972)" class="id">s</span><span class="pn">)</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs218', 973)" onmouseover="showTip(event, 'fs218', 973)" class="fn">list</span> <span onmouseout="hideTip(event, 'fs237', 974)" onmouseover="showTip(event, 'fs237', 974)" class="id">d</span> <span onmouseout="hideTip(event, 'fs238', 975)" onmouseover="showTip(event, 'fs238', 975)" class="id">s</span><span class="pn">)</span> <span class="pn">[</span><span onmouseout="hideTip(event, 'fs214', 976)" onmouseover="showTip(event, 'fs214', 976)" class="uc">No</span><span class="pn">;</span><span onmouseout="hideTip(event, 'fs215', 977)" onmouseover="showTip(event, 'fs215', 977)" class="uc">Part</span><span class="pn">;</span><span onmouseout="hideTip(event, 'fs216', 978)" onmouseover="showTip(event, 'fs216', 978)" class="uc">Yes</span><span class="pn">]</span><span class="pn">)</span>
<span class="pn">[</span><span onmouseout="hideTip(event, 'fs210', 979)" onmouseover="showTip(event, 'fs210', 979)" class="uc">Low</span><span class="pn">;</span><span onmouseout="hideTip(event, 'fs211', 980)" onmouseover="showTip(event, 'fs211', 980)" class="uc">Medium</span><span class="pn">;</span><span onmouseout="hideTip(event, 'fs212', 981)" onmouseover="showTip(event, 'fs212', 981)" class="uc">High</span><span class="pn">]</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs54', 982)" onmouseover="showTip(event, 'fs54', 982)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs232', 983)" onmouseover="showTip(event, 'fs232', 983)" class="id">iter</span> <span class="pn">(</span><span class="k">fun</span> <span class="pn">(</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs237', 984)" onmouseover="showTip(event, 'fs237', 984)" class="id">d</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs238', 985)" onmouseover="showTip(event, 'fs238', 985)" class="id">s</span><span class="pn">)</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs239', 986)" onmouseover="showTip(event, 'fs239', 986)" class="id">lists</span><span class="pn">)</span> <span class="k">-></span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs240', 987)" onmouseover="showTip(event, 'fs240', 987)" class="fn">timeStatistics</span> <span onmouseout="hideTip(event, 'fs241', 988)" onmouseover="showTip(event, 'fs241', 988)" class="fn">f</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs242', 989)" onmouseover="showTip(event, 'fs242', 989)" class="m">Performance</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs143', 990)" onmouseover="showTip(event, 'fs143', 990)" class="id">timeStatistics</span>
<span class="pn">(</span><span class="k">fun</span> <span onmouseout="hideTip(event, 'fs243', 991)" onmouseover="showTip(event, 'fs243', 991)" class="fn">timer</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs54', 992)" onmouseover="showTip(event, 'fs54', 992)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs232', 993)" onmouseover="showTip(event, 'fs232', 993)" class="id">iter</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs204', 994)" onmouseover="showTip(event, 'fs204', 994)" class="m">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs207', 995)" onmouseover="showTip(event, 'fs207', 995)" class="id">toArray</span> <span class="o">></span><span class="pn">></span> <span onmouseout="hideTip(event, 'fs243', 996)" onmouseover="showTip(event, 'fs243', 996)" class="fn">timer</span> <span onmouseout="hideTip(event, 'fs241', 997)" onmouseover="showTip(event, 'fs241', 997)" class="fn">f</span> <span class="o">></span><span class="pn">></span> <span onmouseout="hideTip(event, 'fs120', 998)" onmouseover="showTip(event, 'fs120', 998)" class="fn">ignore</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs239', 999)" onmouseover="showTip(event, 'fs239', 999)" class="id">lists</span><span class="pn">)</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs244', 1000)" onmouseover="showTip(event, 'fs244', 1000)" class="id">p1</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs240', 1001)" onmouseover="showTip(event, 'fs240', 1001)" class="fn">timeStatistics</span> <span onmouseout="hideTip(event, 'fs208', 1002)" onmouseover="showTip(event, 'fs208', 1002)" class="m">Statistics</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs186', 1003)" onmouseover="showTip(event, 'fs186', 1003)" class="id">medianInPlace</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs245', 1004)" onmouseover="showTip(event, 'fs245', 1004)" class="id">p2</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs240', 1005)" onmouseover="showTip(event, 'fs240', 1005)" class="fn">timeStatistics</span> <span onmouseout="hideTip(event, 'fs194', 1006)" onmouseover="showTip(event, 'fs194', 1006)" class="fn">medianQuickSelect</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs246', 1007)" onmouseover="showTip(event, 'fs246', 1007)" class="id">p3</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs240', 1008)" onmouseover="showTip(event, 'fs240', 1008)" class="fn">timeStatistics</span> <span onmouseout="hideTip(event, 'fs196', 1009)" onmouseover="showTip(event, 'fs196', 1009)" class="fn">medianFullSort</span>
<span onmouseout="hideTip(event, 'fs235', 1010)" onmouseover="showTip(event, 'fs235', 1010)" class="fn">printfn</span>
<span class="s">"| </span><span class="pf">%-6s</span><span class="s"> | </span><span class="pf">%-4s</span><span class="s"> | 1.000 | </span><span class="pf">%6.3f</span><span class="s"> | </span><span class="pf">%6.3f</span><span class="s"> | </span><span class="pf">%.4f</span><span class="s">s |"</span>
<span class="pn">(</span><span onmouseout="hideTip(event, 'fs247', 1011)" onmouseover="showTip(event, 'fs247', 1011)" class="fn">string</span> <span onmouseout="hideTip(event, 'fs237', 1012)" onmouseover="showTip(event, 'fs237', 1012)" class="id">d</span><span class="pn">)</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs247', 1013)" onmouseover="showTip(event, 'fs247', 1013)" class="fn">string</span> <span onmouseout="hideTip(event, 'fs238', 1014)" onmouseover="showTip(event, 'fs238', 1014)" class="id">s</span><span class="pn">)</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs245', 1015)" onmouseover="showTip(event, 'fs245', 1015)" class="id">p2</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs40', 1016)" onmouseover="showTip(event, 'fs40', 1016)" class="id">Mean</span><span class="o">/</span><span onmouseout="hideTip(event, 'fs244', 1017)" onmouseover="showTip(event, 'fs244', 1017)" class="id">p1</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs40', 1018)" onmouseover="showTip(event, 'fs40', 1018)" class="id">Mean</span><span class="pn">)</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs246', 1019)" onmouseover="showTip(event, 'fs246', 1019)" class="id">p3</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs40', 1020)" onmouseover="showTip(event, 'fs40', 1020)" class="id">Mean</span><span class="o">/</span><span onmouseout="hideTip(event, 'fs244', 1021)" onmouseover="showTip(event, 'fs244', 1021)" class="id">p1</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs40', 1022)" onmouseover="showTip(event, 'fs40', 1022)" class="id">Mean</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs244', 1023)" onmouseover="showTip(event, 'fs244', 1023)" class="id">p1</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs40', 1024)" onmouseover="showTip(event, 'fs40', 1024)" class="id">Mean</span>
<span class="pn">)</span>
</code></pre>
<table>
<thead>
<tr class="header">
<th align="center"><p>Duplication</p></th>
<th align="center"><p>Sorted</p></th>
<th align="center"><p>Current</p></th>
<th align="center"><p>MathNet</p></th>
<th align="center"><p>FullSort</p></th>
<th align="center"><p>1.000 =</p></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td align="center"><p>Low</p></td>
<td align="center"><p>No</p></td>
<td align="center"><p>1.000</p></td>
<td align="center"><p>1.369</p></td>
<td align="center"><p>6.582</p></td>
<td align="center"><p>0.1028s</p></td>
</tr>
<tr class="even">
<td align="center"><p>Low</p></td>
<td align="center"><p>Part</p></td>
<td align="center"><p>1.000</p></td>
<td align="center"><p>1.350</p></td>
<td align="center"><p>9.269</p></td>
<td align="center"><p>0.0476s</p></td>
</tr>
<tr class="odd">
<td align="center"><p>Low</p></td>
<td align="center"><p>Yes</p></td>
<td align="center"><p>1.000</p></td>
<td align="center"><p>1.516</p></td>
<td align="center"><p>12.964</p></td>
<td align="center"><p>0.0106s</p></td>
</tr>
<tr class="even">
<td align="center"><p>Medium</p></td>
<td align="center"><p>No</p></td>
<td align="center"><p>1.000</p></td>
<td align="center"><p>1.373</p></td>
<td align="center"><p>6.577</p></td>
<td align="center"><p>0.1018s</p></td>
</tr>
<tr class="odd">
<td align="center"><p>Medium</p></td>
<td align="center"><p>Part</p></td>
<td align="center"><p>1.000</p></td>
<td align="center"><p>1.397</p></td>
<td align="center"><p>9.471</p></td>
<td align="center"><p>0.0478s</p></td>
</tr>
<tr class="even">
<td align="center"><p>Medium</p></td>
<td align="center"><p>Yes</p></td>
<td align="center"><p>1.000</p></td>
<td align="center"><p>1.840</p></td>
<td align="center"><p>17.519</p></td>
<td align="center"><p>0.0100s</p></td>
</tr>
<tr class="odd">
<td align="center"><p>High</p></td>
<td align="center"><p>No</p></td>
<td align="center"><p>1.000</p></td>
<td align="center"><p>1.341</p></td>
<td align="center"><p>4.745</p></td>
<td align="center"><p>0.1059s</p></td>
</tr>
<tr class="even">
<td align="center"><p>High</p></td>
<td align="center"><p>Part</p></td>
<td align="center"><p>1.000</p></td>
<td align="center"><p>1.576</p></td>
<td align="center"><p>8.050</p></td>
<td align="center"><p>0.0526s</p></td>
</tr>
<tr class="odd">
<td align="center"><p>High</p></td>
<td align="center"><p>Yes</p></td>
<td align="center"><p>1.000</p></td>
<td align="center"><p>3.193</p></td>
<td align="center"><p>26.390</p></td>
<td align="center"><p>0.0087s</p></td>
</tr>
</tbody>
</table>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p>Optimised generic select, median and median absolute deviation functions have been developed.</p>
<p>The performance results show a good improvement over Quickselect which is already an optimised algorithm.
The performance of the code is also more predictable due to its handling of duplication and partially sorted data.</p>
<p>This also demonstrates how property based testing and a performance testing library can be used together to optimise algorithms.</p>
<div class="tip" id="fs1">namespace System</div>
<div class="tip" id="fs2">namespace System.Diagnostics</div>
<div class="tip" id="fs3">type PropertyAttribute = | FactAttribute</div>
<div class="tip" id="fs4">union case PropertyAttribute.FactAttribute: PropertyAttribute</div>
<div class="tip" id="fs5">Multiple items<br />type AutoOpenAttribute =<br />  inherit Attribute<br />  new : unit -> AutoOpenAttribute<br />  new : path:string -> AutoOpenAttribute<br />  member Path : string<br /><br />--------------------<br />new : unit -> AutoOpenAttribute<br />new : path:string -> AutoOpenAttribute</div>
<div class="tip" id="fs6">val sqr : x:'a -> 'b (requires member ( * ))</div>
<div class="tip" id="fs7">val x : 'a (requires member ( * ))</div>
<div class="tip" id="fs8">Multiple items<br />union case MinValueGen.MinValueGen: MinValueGen<br /><br />--------------------<br />type MinValueGen =<br />  | MinValueGen<br />    static member ( => ) : MinValueGen * int -> int<br />    static member ( => ) : MinValueGen * int64 -> int64<br />    static member ( => ) : MinValueGen * float -> float<br />    static member ( => ) : MinValueGen * MinValueGen -> 'a</div>
<div class="tip" id="fs9">Multiple items<br />val int : value:'T -> int (requires member op_Explicit)<br /><br />--------------------<br />type int = int32<br /><br />--------------------<br />type int<'Measure> = int</div>
<div class="tip" id="fs10">type Int32 =<br />  struct<br />    member CompareTo : value:obj -> int + 1 overload<br />    member Equals : obj:obj -> bool + 1 overload<br />    member GetHashCode : unit -> int<br />    member GetTypeCode : unit -> TypeCode<br />    member ToString : unit -> string + 3 overloads<br />    static val MaxValue : int<br />    static val MinValue : int<br />    static member Parse : s:string -> int + 3 overloads<br />    static member TryParse : s:string * result:int -> bool + 1 overload<br />  end</div>
<div class="tip" id="fs11">field int.MinValue: int = -2147483648</div>
<div class="tip" id="fs12">Multiple items<br />val int64 : value:'T -> int64 (requires member op_Explicit)<br /><br />--------------------<br />type int64 = Int64<br /><br />--------------------<br />type int64<'Measure> = int64</div>
<div class="tip" id="fs13">type Int64 =<br />  struct<br />    member CompareTo : value:obj -> int + 1 overload<br />    member Equals : obj:obj -> bool + 1 overload<br />    member GetHashCode : unit -> int<br />    member GetTypeCode : unit -> TypeCode<br />    member ToString : unit -> string + 3 overloads<br />    static val MaxValue : int64<br />    static val MinValue : int64<br />    static member Parse : s:string -> int64 + 3 overloads<br />    static member TryParse : s:string * result:int64 -> bool + 1 overload<br />  end</div>
<div class="tip" id="fs14">field int64.MinValue: int64 = -9223372036854775808L</div>
<div class="tip" id="fs15">Multiple items<br />val float : value:'T -> float (requires member op_Explicit)<br /><br />--------------------<br />type float = Double<br /><br />--------------------<br />type float<'Measure> = float</div>
<div class="tip" id="fs16">type Double =<br />  struct<br />    member CompareTo : value:obj -> int + 1 overload<br />    member Equals : obj:obj -> bool + 1 overload<br />    member GetHashCode : unit -> int<br />    member GetTypeCode : unit -> TypeCode<br />    member ToString : unit -> string + 3 overloads<br />    static val MinValue : float<br />    static val MaxValue : float<br />    static val Epsilon : float<br />    static val NegativeInfinity : float<br />    static val PositiveInfinity : float<br />    ...<br />  end</div>
<div class="tip" id="fs17">field float.MinValue: float = -1.79769313486e+308</div>
<div class="tip" id="fs18">val failwith : message:string -> 'T</div>
<div class="tip" id="fs19">val minValue : unit -> 'a (requires member get_Zero and member ( => ))<br /><em><br /><br /> Returns the minimum value for a generic type.</em></div>
<div class="tip" id="fs20">module LanguagePrimitives<br /><br />from Microsoft.FSharp.Core</div>
<div class="tip" id="fs21">val GenericZero<'T (requires member get_Zero)> : 'T (requires member get_Zero)</div>
<div class="tip" id="fs22">Multiple items<br />union case MaxValueGen.MaxValueGen: MaxValueGen<br /><br />--------------------<br />type MaxValueGen =<br />  | MaxValueGen<br />    static member ( => ) : MaxValueGen * int -> int<br />    static member ( => ) : MaxValueGen * int64 -> int64<br />    static member ( => ) : MaxValueGen * float -> float<br />    static member ( => ) : MaxValueGen * MaxValueGen -> 'a</div>
<div class="tip" id="fs23">field int.MaxValue: int = 2147483647</div>
<div class="tip" id="fs24">field int64.MaxValue: int64 = 9223372036854775807L</div>
<div class="tip" id="fs25">field float.MaxValue: float = 1.79769313486e+308</div>
<div class="tip" id="fs26">val maxValue : unit -> 'a (requires member get_Zero and member ( => ))<br /><em><br /><br /> Returns the maximum value for a generic type.</em></div>
<div class="tip" id="fs27">Multiple items<br />union case HalfPositiveGen.HalfPositiveGen: HalfPositiveGen<br /><br />--------------------<br />type HalfPositiveGen =<br />  | HalfPositiveGen<br />    static member ( => ) : HalfPositiveGen * x:int -> int<br />    static member ( => ) : HalfPositiveGen * x:int64 -> int64<br />    static member ( => ) : HalfPositiveGen * x:float -> float<br />    static member ( => ) : HalfPositiveGen * HalfPositiveGen -> 'a</div>
<div class="tip" id="fs28">val x : int</div>
<div class="tip" id="fs29">val x : int64</div>
<div class="tip" id="fs30">val x : float</div>
<div class="tip" id="fs31">val halfPositive : x:'a -> 'a (requires member ( => ))<br /><em><br /><br /> Returns half of the positive generic input value.</em></div>
<div class="tip" id="fs32">val x : 'a (requires member ( => ))</div>
<div class="tip" id="fs33">val middleOrdered : a:'a -> b:'c -> 'd (requires member ( + ) and member ( - ) and member ( => ))<br /><em><br /><br /> Returns the middle point of two ordered input values.</em></div>
<div class="tip" id="fs34">val a : 'a (requires member ( + ) and member ( - ) and member ( => ))</div>
<div class="tip" id="fs35">val b : 'c (requires member ( - ) and member ( + ) and member ( => ))</div>
<div class="tip" id="fs36">val middle : a:'a -> b:'a -> 'c (requires comparison and member ( + ) and member ( - ) and member ( => ))<br /><em><br /><br /> Returns the middle point of two input values.</em></div>
<div class="tip" id="fs37">val a : 'a (requires comparison and member ( + ) and member ( - ) and member ( => ))</div>
<div class="tip" id="fs38">val b : 'a (requires comparison and member ( + ) and member ( - ) and member ( => ))</div>
<div class="tip" id="fs39">SampleStatistics.N: int</div>
<div class="tip" id="fs40">SampleStatistics.Mean: float</div>
<div class="tip" id="fs41">SampleStatistics.Variance: float</div>
<div class="tip" id="fs42">val s : SampleStatistics</div>
<div class="tip" id="fs43">val sqrt : value:'T -> 'U (requires member Sqrt)</div>
<div class="tip" id="fs44">type WelchStatistic =<br />  {T: float;<br />   DF: int;}</div>
<div class="tip" id="fs45">WelchStatistic.T: float</div>
<div class="tip" id="fs46">WelchStatistic.DF: int</div>
<div class="tip" id="fs47">val sampleStatistics : s:seq<'a> -> seq<SampleStatistics> (requires member op_Explicit)<br /><em><br /><br /> Online statistics sequence for a given sample sequence.</em></div>
<div class="tip" id="fs48">val s : seq<'a> (requires member op_Explicit)</div>
<div class="tip" id="fs49">val calc : (int * float * float -> float -> int * float * float)</div>
<div class="tip" id="fs50">val n : int</div>
<div class="tip" id="fs51">val m : float</div>
<div class="tip" id="fs52">val s : float</div>
<div class="tip" id="fs53">val m' : float</div>
<div class="tip" id="fs54">module Seq<br /><br />from Microsoft.FSharp.Collections</div>
<div class="tip" id="fs55">val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U></div>
<div class="tip" id="fs56">val scan : folder:('State -> 'T -> 'State) -> state:'State -> source:seq<'T> -> seq<'State></div>
<div class="tip" id="fs57">val skip : count:int -> source:seq<'T> -> seq<'T></div>
<div class="tip" id="fs58">val scale : f:float -> s:SampleStatistics -> SampleStatistics<br /><em><br /><br /> Scale the statistics for a given underlying random variable change of scale.</em></div>
<div class="tip" id="fs59">val f : float</div>
<div class="tip" id="fs60">val singleIteration : ic:int -> s:SampleStatistics -> SampleStatistics<br /><em><br /><br /> Single iteration statistics for a given iteration count and total statistics.</em></div>
<div class="tip" id="fs61">val ic : int</div>
<div class="tip" id="fs62">val private tInv : float []<br /><em><br /><br /> Student's t-distribution inverse for 0.1% confidence level by degrees of freedom.</em></div>
<div class="tip" id="fs63">val welchStatistic : s1:SampleStatistics -> s2:SampleStatistics -> WelchStatistic<br /><em><br /><br /> Welch's t-test statistic for two given sample statistics.</em></div>
<div class="tip" id="fs64">val s1 : SampleStatistics</div>
<div class="tip" id="fs65">val s2 : SampleStatistics</div>
<div class="tip" id="fs66">val f1 : float</div>
<div class="tip" id="fs67">val f2 : float</div>
<div class="tip" id="fs68">val welchTest : w:WelchStatistic -> int<br /><em><br /><br /> Welch's t-test for a given Welch statistic to a confidence level of 0.1%.</em></div>
<div class="tip" id="fs69">val w : WelchStatistic</div>
<div class="tip" id="fs70">val abs : value:'T -> 'T (requires member Abs)</div>
<div class="tip" id="fs71">type Array =<br />  member Clone : unit -> obj<br />  member CopyTo : array:Array * index:int -> unit + 1 overload<br />  member GetEnumerator : unit -> IEnumerator<br />  member GetLength : dimension:int -> int<br />  member GetLongLength : dimension:int -> int64<br />  member GetLowerBound : dimension:int -> int<br />  member GetUpperBound : dimension:int -> int<br />  member GetValue : [<ParamArray>] indices:int[] -> obj + 7 overloads<br />  member Initialize : unit -> unit<br />  member IsFixedSize : bool<br />  ...</div>
<div class="tip" id="fs72">val get : array:'T [] -> index:int -> 'T</div>
<div class="tip" id="fs73">val min : e1:'T -> e2:'T -> 'T (requires comparison)</div>
<div class="tip" id="fs74">val length : array:'T [] -> int</div>
<div class="tip" id="fs75">val sign : value:'T -> int (requires member get_Sign)</div>
<div class="tip" id="fs76">module StatisticsPerf<br /><br />from Main</div>
<div class="tip" id="fs77">val private targetIterationCount : metric:(int -> 'a -> 'b) -> metricTarget:'b -> f:'a -> int (requires member ( <<< ) and comparison and member op_Explicit)<br /><em><br /><br /> Find the iteration count to get to at least the metric target.</em></div>
<div class="tip" id="fs78">val metric : (int -> 'a -> 'b) (requires member ( <<< ) and comparison and member op_Explicit)</div>
<div class="tip" id="fs79">val metricTarget : 'b (requires member ( <<< ) and comparison and member op_Explicit)</div>
<div class="tip" id="fs80">val f : 'a</div>
<div class="tip" id="fs81">val find : (int -> int)</div>
<div class="tip" id="fs82">val item : 'b (requires member ( <<< ) and comparison and member op_Explicit)</div>
<div class="tip" id="fs83">val private measureStatistics : metric:(int -> 'a -> 'b) * metricTarget:'b -> relativeError:float -> f:'a -> SampleStatistics (requires member ( <<< ) and comparison and member op_Explicit and member op_Explicit)<br /><em><br /><br /> Create and iterate a statistics sequence for a metric until the given accuracy.</em></div>
<div class="tip" id="fs84">val metric : (int -> 'a -> 'b) (requires member ( <<< ) and comparison and member op_Explicit and member op_Explicit)</div>
<div class="tip" id="fs85">val metricTarget : 'b (requires member ( <<< ) and comparison and member op_Explicit and member op_Explicit)</div>
<div class="tip" id="fs86">val relativeError : float</div>
<div class="tip" id="fs87">val initInfinite : initializer:(int -> 'T) -> seq<'T></div>
<div class="tip" id="fs88">val find : predicate:('T -> bool) -> source:seq<'T> -> 'T</div>
<div class="tip" id="fs89">property SampleStatistics.MeanStandardError: float</div>
<div class="tip" id="fs90">val private measureCompare : metric:(int -> (('a -> 'a) -> 'b) -> 'c) * metricTarget:'c -> f1:(('a -> 'a) -> 'b) -> f2:(('a -> 'a) -> 'b) -> int (requires equality and member ( <<< ) and comparison and member op_Explicit and member op_Explicit)<br /><em><br /><br /> Create and iterate two statistics sequences until the metric means can be compared.</em></div>
<div class="tip" id="fs91">val metric : (int -> (('a -> 'a) -> 'b) -> 'c) (requires equality and member ( <<< ) and comparison and member op_Explicit and member op_Explicit)</div>
<div class="tip" id="fs92">val metricTarget : 'c (requires member ( <<< ) and comparison and member op_Explicit and member op_Explicit)</div>
<div class="tip" id="fs93">val f1 : (('a -> 'a) -> 'b) (requires equality)</div>
<div class="tip" id="fs94">val f2 : (('a -> 'a) -> 'b) (requires equality)</div>
<div class="tip" id="fs95">val id : x:'T -> 'T</div>
<div class="tip" id="fs96">val stats : ((('a -> 'a) -> 'b) -> seq<SampleStatistics>) (requires equality)</div>
<div class="tip" id="fs97">val f : (('a -> 'a) -> 'b) (requires equality)</div>
<div class="tip" id="fs98">val map2 : mapping:('T1 -> 'T2 -> 'U) -> source1:seq<'T1> -> source2:seq<'T2> -> seq<'U></div>
<div class="tip" id="fs99">val pick : chooser:('T -> 'U option) -> source:seq<'T> -> 'U</div>
<div class="tip" id="fs100">val maxDF : int</div>
<div class="tip" id="fs101">union case Option.Some: Value: 'T -> Option<'T></div>
<div class="tip" id="fs102">union case Option.None: Option<'T></div>
<div class="tip" id="fs103">val c : int</div>
<div class="tip" id="fs104">val private measureMetric : startMetric:(unit -> 'a) -> endMetric:('a -> 'b) -> ic:int -> f:((('d -> 'e) -> 'd -> 'e) -> 'f) -> 'c (requires member ( + ) and member get_Zero)<br /><em><br /><br /> Measure the given function for the iteration count using the start and end metric.</em></div>
<div class="tip" id="fs105">val startMetric : (unit -> 'a)</div>
<div class="tip" id="fs106">val endMetric : ('a -> 'b) (requires member ( + ) and member get_Zero)</div>
<div class="tip" id="fs107">val f : ((('d -> 'e) -> 'd -> 'e) -> 'f)</div>
<div class="tip" id="fs108">type GC =<br />  static member AddMemoryPressure : bytesAllocated:int64 -> unit<br />  static member CancelFullGCNotification : unit -> unit<br />  static member Collect : unit -> unit + 4 overloads<br />  static member CollectionCount : generation:int -> int<br />  static member EndNoGCRegion : unit -> unit<br />  static member GetGeneration : obj:obj -> int + 1 overload<br />  static member GetTotalMemory : forceFullCollection:bool -> int64<br />  static member KeepAlive : obj:obj -> unit<br />  static member MaxGeneration : int<br />  static member ReRegisterForFinalize : obj:obj -> unit<br />  ...</div>
<div class="tip" id="fs109">GC.Collect() : unit<br />GC.Collect(generation: int) : unit<br />GC.Collect(generation: int, mode: GCCollectionMode) : unit<br />GC.Collect(generation: int, mode: GCCollectionMode, blocking: bool) : unit<br />GC.Collect(generation: int, mode: GCCollectionMode, blocking: bool, compacting: bool) : unit</div>
<div class="tip" id="fs110">GC.WaitForPendingFinalizers() : unit</div>
<div class="tip" id="fs111">val mutable total : 'c (requires member get_Zero and member ( + ))</div>
<div class="tip" id="fs112">val measurer : (('g -> 'h) -> 'g -> 'h)</div>
<div class="tip" id="fs113">val toMeasure : ('g -> 'h)</div>
<div class="tip" id="fs114">val args : 'g</div>
<div class="tip" id="fs115">val s : 'a</div>
<div class="tip" id="fs116">val ret : 'h</div>
<div class="tip" id="fs117">val m : 'b (requires member ( + ) and member get_Zero)</div>
<div class="tip" id="fs118">val loop : (int -> unit)</div>
<div class="tip" id="fs119">val i : int</div>
<div class="tip" id="fs120">val ignore : value:'T -> unit</div>
<div class="tip" id="fs121">val private timeMetric : ic:int -> f:((('a -> 'b) -> 'a -> 'b) -> 'c) -> int64<br /><em><br /><br /> Measure the time metric for the given function and iteration count.</em></div>
<div class="tip" id="fs122">val f : ((('a -> 'b) -> 'a -> 'b) -> 'c)</div>
<div class="tip" id="fs123">Multiple items<br />type Stopwatch =<br />  new : unit -> Stopwatch<br />  member Elapsed : TimeSpan<br />  member ElapsedMilliseconds : int64<br />  member ElapsedTicks : int64<br />  member IsRunning : bool<br />  member Reset : unit -> unit<br />  member Restart : unit -> unit<br />  member Start : unit -> unit<br />  member Stop : unit -> unit<br />  static val Frequency : int64<br />  ...<br /><br />--------------------<br />Stopwatch() : Stopwatch</div>
<div class="tip" id="fs124">Stopwatch.GetTimestamp() : int64</div>
<div class="tip" id="fs125">val t : int64</div>
<div class="tip" id="fs126">val private memoryMetric : ic:int -> f:((('a -> 'b) -> 'a -> 'b) -> 'c) -> int64<br /><em><br /><br /> Measure the memory metric for the given function and iteration count.</em></div>
<div class="tip" id="fs127">val startMetric : (unit -> int64)</div>
<div class="tip" id="fs128">GC.TryStartNoGCRegion(totalSize: int64) : bool<br />GC.TryStartNoGCRegion(totalSize: int64, disallowFullBlockingGC: bool) : bool<br />GC.TryStartNoGCRegion(totalSize: int64, lohSize: int64) : bool<br />GC.TryStartNoGCRegion(totalSize: int64, lohSize: int64, disallowFullBlockingGC: bool) : bool</div>
<div class="tip" id="fs129">val not : value:bool -> bool</div>
<div class="tip" id="fs130">GC.GetTotalMemory(forceFullCollection: bool) : int64</div>
<div class="tip" id="fs131">val endMetric : (int64 -> int64)</div>
<div class="tip" id="fs132">val s : int64</div>
<div class="tip" id="fs133">GC.EndNoGCRegion() : unit</div>
<div class="tip" id="fs134">val private garbageMetric : ic:int -> f:((('a -> 'b) -> 'a -> 'b) -> 'c) -> int<br /><em><br /><br /> Measure the garbage collection metric for the given function and iteration count.</em></div>
<div class="tip" id="fs135">val count : (unit -> int)</div>
<div class="tip" id="fs136">GC.CollectionCount(generation: int) : int</div>
<div class="tip" id="fs137">val s : int</div>
<div class="tip" id="fs138">val private oneMillisecond : int64<br /><em><br /><br /> Measure definitions which are a metric together with a metric target.</em></div>
<div class="tip" id="fs139">field Stopwatch.Frequency: int64</div>
<div class="tip" id="fs140">val private timeMeasure : (int -> ((('a -> 'b) -> 'a -> 'b) -> 'c) -> int64) * int64</div>
<div class="tip" id="fs141">val private memoryMeasure : (int -> ((('a -> 'b) -> 'a -> 'b) -> 'c) -> int64) * int64</div>
<div class="tip" id="fs142">val private garbageMeasure : (int -> ((('a -> 'b) -> 'a -> 'b) -> 'c) -> int) * int</div>
<div class="tip" id="fs143">val timeStatistics : f:((('a -> 'b) -> 'a -> 'b) -> 'c) -> SampleStatistics<br /><em><br /><br /> Time statistics for a given function accurate to a mean standard error of 1%.</em></div>
<div class="tip" id="fs144">val memoryStatistics : f:((('a -> 'b) -> 'a -> 'b) -> 'c) -> SampleStatistics<br /><em><br /><br /> Memory statistics for a given function accurate to a mean standard error of 1%.</em></div>
<div class="tip" id="fs145">val gcCountStatistics : f:((('a -> 'b) -> 'a -> 'b) -> 'c) -> SampleStatistics<br /><em><br /><br /> GC count statistics for a given function accurate to a mean standard error of 1%.</em></div>
<div class="tip" id="fs146">val timeCompare : f1:((('a -> 'b) -> 'a -> 'b) -> 'c) -> f2:((('a -> 'b) -> 'a -> 'b) -> 'c) -> int (requires equality)<br /><em><br /><br /> Time comparison for two given functions to a confidence level of 0.1%.</em></div>
<div class="tip" id="fs147">val f1 : ((('a -> 'b) -> 'a -> 'b) -> 'c) (requires equality)</div>
<div class="tip" id="fs148">val f2 : ((('a -> 'b) -> 'a -> 'b) -> 'c) (requires equality)</div>
<div class="tip" id="fs149">val memoryCompare : f1:((('a -> 'b) -> 'a -> 'b) -> 'c) -> f2:((('a -> 'b) -> 'a -> 'b) -> 'c) -> int (requires equality)<br /><em><br /><br /> Memory comparison for two given functions to a confidence level of 0.1%.</em></div>
<div class="tip" id="fs150">val gcCountCompare : f1:((('a -> 'b) -> 'a -> 'b) -> 'c) -> f2:((('a -> 'b) -> 'a -> 'b) -> 'c) -> int (requires equality)<br /><em><br /><br /> GC count comparison for two given functions to a confidence level of 0.1%.</em></div>
<div class="tip" id="fs151">val median3 : a:'a -> b:'a -> c:'a -> 'a (requires comparison)<br /><em><br /><br /> Returns the median of three input values.</em></div>
<div class="tip" id="fs152">val a : 'a (requires comparison)</div>
<div class="tip" id="fs153">val b : 'a (requires comparison)</div>
<div class="tip" id="fs154">val c : 'a (requires comparison)</div>
<div class="tip" id="fs155">val minSub : a:'a [] -> lo:int -> hi:int -> 'a (requires comparison)<br /><em><br /><br /> Returns the minimum value of a subsection of an array.</em></div>
<div class="tip" id="fs156">val a : 'a [] (requires comparison)</div>
<div class="tip" id="fs157">val lo : int</div>
<div class="tip" id="fs158">val hi : int</div>
<div class="tip" id="fs159">val mutable v : 'a (requires comparison)</div>
<div class="tip" id="fs160">val nv : 'a (requires comparison)</div>
<div class="tip" id="fs161">val maxSub : a:'a [] -> lo:int -> hi:int -> 'a (requires comparison)<br /><em><br /><br /> Returns the maximum value of a subsection of an array.</em></div>
<div class="tip" id="fs162">val min2middleSub : a:'a [] -> lo:int -> hi:int -> 'c (requires comparison and member ( + ) and member ( - ) and member ( => ))<br /><em><br /><br /> Returns the middle point of the two smallest values of a subsection of an array.</em></div>
<div class="tip" id="fs163">val a : 'a [] (requires comparison and member ( + ) and member ( - ) and member ( => ))</div>
<div class="tip" id="fs164">val mutable v0 : 'a (requires comparison and member ( + ) and member ( - ) and member ( => ))</div>
<div class="tip" id="fs165">val mutable v1 : 'a (requires comparison and member ( + ) and member ( - ) and member ( => ))</div>
<div class="tip" id="fs166">val tmp : 'a (requires comparison and member ( + ) and member ( - ) and member ( => ))</div>
<div class="tip" id="fs167">val nv : 'a (requires comparison and member ( + ) and member ( - ) and member ( => ))</div>
<div class="tip" id="fs168">val max2middleSub : a:'a [] -> lo:int -> hi:int -> 'c (requires comparison and member ( + ) and member ( - ) and member ( => ))<br /><em><br /><br /> Returns the middle point of the two largest values of a subsection of an array.</em></div>
<div class="tip" id="fs169">val swap : a:'a [] -> i:int -> j:int -> unit<br /><em><br /><br /> Swap two elements in an array.</em></div>
<div class="tip" id="fs170">val a : 'a []</div>
<div class="tip" id="fs171">val j : int</div>
<div class="tip" id="fs172">val temp : 'a</div>
<div class="tip" id="fs173">val swapIf : a:'a [] -> i:int -> j:int -> unit (requires comparison)<br /><em><br /><br /> Swap two elements in an array if the first is larger than the second.</em></div>
<div class="tip" id="fs174">val ai : 'a (requires comparison)</div>
<div class="tip" id="fs175">val aj : 'a (requires comparison)</div>
<div class="tip" id="fs176">val selectInPlace : a:'a [] -> k:int -> middleNext:bool -> 'a (requires comparison and member get_Zero and member ( => ) and member ( => ) and member ( - ) and member ( + ) and member ( => ))<br /><em><br /><br /> Returns the kth smallest element in an array and optionally the middle with<br /> the next largest. Elements will be reordered in place and cannot be equal<br /> to the max or min value of the generic type.</em></div>
<div class="tip" id="fs177">val a : 'a [] (requires comparison and member get_Zero and member ( => ) and member ( => ) and member ( - ) and member ( + ) and member ( => ))</div>
<div class="tip" id="fs178">val k : int</div>
<div class="tip" id="fs179">val middleNext : bool</div>
<div class="tip" id="fs180">val outerLoop : (int -> int -> 'a) (requires comparison and member get_Zero and member ( => ) and member ( => ) and member ( - ) and member ( + ) and member ( => ))</div>
<div class="tip" id="fs181">val pivot : 'a (requires comparison and member get_Zero and member ( => ) and member ( => ) and member ( - ) and member ( + ) and member ( => ))</div>
<div class="tip" id="fs182">val resetLo : bool</div>
<div class="tip" id="fs183">val resetHi : bool</div>
<div class="tip" id="fs184">val mutable i : int</div>
<div class="tip" id="fs185">val mutable j : int</div>
<div class="tip" id="fs186">val medianInPlace : a:'a [] -> 'a (requires comparison and member ( + ) and member ( - ) and member get_Zero and member ( => ) and member ( => ) and member ( => ))<br /><em><br /><br /> Returns the median of an array. Elements will be reordered in place and<br /> cannot be equal to the max or min value of the generic type.</em></div>
<div class="tip" id="fs187">val a : 'a [] (requires comparison and member ( + ) and member ( - ) and member get_Zero and member ( => ) and member ( => ) and member ( => ))</div>
<div class="tip" id="fs188">val last : int</div>
<div class="tip" id="fs189">val medianAndMAD : a:'a [] -> 'a * 'a (requires comparison and member ( + ) and member get_Zero and member ( => ) and member ( => ) and member Abs and member ( - ) and member ( => ))<br /><em><br /><br /> Returns the median and median absolute deviation of an array.<br /> Elements cannot be equal to the max or min value of the generic type.</em></div>
<div class="tip" id="fs190">val a : 'a [] (requires comparison and member ( + ) and member get_Zero and member ( => ) and member ( => ) and member Abs and member ( - ) and member ( => ))</div>
<div class="tip" id="fs191">val copy : array:'T [] -> 'T []</div>
<div class="tip" id="fs192">val median : 'a (requires comparison and member ( + ) and member get_Zero and member ( => ) and member ( => ) and member Abs and member ( - ) and member ( => ))</div>
<div class="tip" id="fs193">module StatisticsTests<br /><br />from Main</div>
<div class="tip" id="fs194">val medianQuickSelect : a:float [] -> 'a</div>
<div class="tip" id="fs195">val a : float []</div>
<div class="tip" id="fs196">val medianFullSort : a:float [] -> float</div>
<div class="tip" id="fs197">val sortInPlace : array:'T [] -> unit (requires comparison)</div>
<div class="tip" id="fs198">val l : int</div>
<div class="tip" id="fs199">val y : float</div>
<div class="tip" id="fs200">val MedianProp : x:int -> xs:int list -> bool</div>
<div class="tip" id="fs201">val xs : int list</div>
<div class="tip" id="fs202">type 'T list = List<'T></div>
<div class="tip" id="fs203">val l : float list</div>
<div class="tip" id="fs204">Multiple items<br />module List<br /><br />from Microsoft.FSharp.Collections<br /><br />--------------------<br />type List<'T> =<br />  | ( [] )<br />  | ( :: ) of Head: 'T * Tail: 'T list<br />    interface IReadOnlyList<'T><br />    interface IReadOnlyCollection<'T><br />    interface IEnumerable<br />    interface IEnumerable<'T><br />    member GetSlice : startIndex:int option * endIndex:int option -> 'T list<br />    member Head : 'T<br />    member IsEmpty : bool<br />    member Item : index:int -> 'T with get<br />    member Length : int<br />    member Tail : 'T list<br />    ...</div>
<div class="tip" id="fs205">val map : mapping:('T -> 'U) -> list:'T list -> 'U list</div>
<div class="tip" id="fs206">val m1 : float</div>
<div class="tip" id="fs207">val toArray : list:'T list -> 'T []</div>
<div class="tip" id="fs208">module Statistics<br /><br />from Main</div>
<div class="tip" id="fs209">val m2 : float</div>
<div class="tip" id="fs210">union case Duplication.Low: Duplication</div>
<div class="tip" id="fs211">union case Duplication.Medium: Duplication</div>
<div class="tip" id="fs212">union case Duplication.High: Duplication</div>
<div class="tip" id="fs213">val i : Duplication</div>
<div class="tip" id="fs214">union case Sorted.No: Sorted</div>
<div class="tip" id="fs215">union case Sorted.Part: Sorted</div>
<div class="tip" id="fs216">union case Sorted.Yes: Sorted</div>
<div class="tip" id="fs217">val i : Sorted</div>
<div class="tip" id="fs218">Multiple items<br />val list : duplication:Duplication -> sorted:Sorted -> seq<float list><br /><br />--------------------<br />type 'T list = List<'T></div>
<div class="tip" id="fs219">val duplication : Duplication</div>
<div class="tip" id="fs220">type Duplication =<br />  | Low<br />  | Medium<br />  | High<br />    override ToString : unit -> string<br />    member ToInt : int</div>
<div class="tip" id="fs221">val sorted : Sorted</div>
<div class="tip" id="fs222">type Sorted =<br />  | No<br />  | Part<br />  | Yes<br />    override ToString : unit -> string</div>
<div class="tip" id="fs223">val r : Random</div>
<div class="tip" id="fs224">Multiple items<br />type Random =<br />  new : unit -> Random + 1 overload<br />  member Next : unit -> int + 2 overloads<br />  member NextBytes : buffer:byte[] -> unit<br />  member NextDouble : unit -> float<br /><br />--------------------<br />Random() : Random<br />Random(Seed: int) : Random</div>
<div class="tip" id="fs225">val next : (unit -> float)</div>
<div class="tip" id="fs226">Random.Next() : int<br />Random.Next(maxValue: int) : int<br />Random.Next(minValue: int, maxValue: int) : int</div>
<div class="tip" id="fs227">property Duplication.ToInt: int</div>
<div class="tip" id="fs228">val init : count:int -> initializer:(int -> 'T) -> seq<'T></div>
<div class="tip" id="fs229">val init : length:int -> initializer:(int -> 'T) -> 'T list</div>
<div class="tip" id="fs230">val sort : list:'T list -> 'T list (requires comparison)</div>
<div class="tip" id="fs231">val len : int</div>
<div class="tip" id="fs232">val iter : action:('T -> unit) -> source:seq<'T> -> unit</div>
<div class="tip" id="fs233">val ofArray : array:'T [] -> 'T list</div>
<div class="tip" id="fs234">val MedianPerfTest : unit -> unit</div>
<div class="tip" id="fs235">val printfn : format:Printf.TextWriterFormat<'T> -> 'T</div>
<div class="tip" id="fs236">val collect : mapping:('T -> #seq<'U>) -> source:seq<'T> -> seq<'U></div>
<div class="tip" id="fs237">val d : Duplication</div>
<div class="tip" id="fs238">val s : Sorted</div>
<div class="tip" id="fs239">val lists : seq<float list></div>
<div class="tip" id="fs240">val timeStatistics : ((float [] -> 'a) -> SampleStatistics)</div>
<div class="tip" id="fs241">val f : (float [] -> 'a)</div>
<div class="tip" id="fs242">module Performance<br /><br />from Main</div>
<div class="tip" id="fs243">val timer : ((float [] -> 'a) -> float [] -> 'a)</div>
<div class="tip" id="fs244">val p1 : SampleStatistics</div>
<div class="tip" id="fs245">val p2 : SampleStatistics</div>
<div class="tip" id="fs246">val p3 : SampleStatistics</div>
<div class="tip" id="fs247">Multiple items<br />val string : value:'T -> string<br /><br />--------------------<br />type string = String</div>
Managing Complexity - Or "Why do you code in F#?"
2016-09-09T00:00:00+01:00
http://anthonylloyd.github.io/blog/2016/09/09/managing-complexity
<p>This post outlines my views on the often overlooked and misunderstood topic of managing complexity in software development.</p>
<p>It answers questions I'm asked sometimes on why I prefer to develop systems in F#, a strongly typed functional-first language.</p>
<p>The next time someone asks I can refer them here!</p>
<h2><a name="The-Questions" class="anchor" href="#The-Questions">The Questions</a></h2>
<ul>
<li>Why don't you program in a language like C++ that has better performance?</li>
<li>What is this functional programming and why would you want to use it?</li>
<li>Isn't it hard to hire F# developers? Wouldn't it be easier to stick to more standard C#?</li>
<li>Why not use something like Python that has lots of libraries to quickly build things?</li>
</ul>
<h2><a name="The-Answer" class="anchor" href="#The-Answer">The Answer</a></h2>
<p>The answer is I want to reduce and control complexity.</p>
<p>Simplicity and the flexibility it brings increases the chance of discovering the best abstraction for the domain.
The classic example of this is 'everything is a file' in Unix.
If you find this for your domain, it will put you streets ahead.
Now you are minimising the inherent complexity of the domain.</p>
<blockquote>
<p>Simplicity is the ultimate sophistication.<br />
<cite>Leonardo da Vinci</cite></p>
</blockquote>
<p>Software languages and frameworks bring with them different degrees of accidental complexity.</p>
<p>Is the problem you are solving simple enough that you can handle this additional complexity?
Are you sure that will always be the case?</p>
<blockquote>
<p>The primary cause of software project failure is complexity.<br />
<cite>Roger Sessions</cite></p>
</blockquote>
<h2><a name="Accidental-Complexity" class="anchor" href="#Accidental-Complexity">Accidental Complexity</a></h2>
<p>The more I use a functional language the longer the list of patterns I consider to have excessive accidental complexity:</p>
<ul>
<li>Object oriented programming</li>
<li>GOF patterns</li>
<li>SOLID patterns</li>
<li>Null</li>
<li>Exceptions</li>
<li>Circular dependencies</li>
<li>Tests rather than types</li>
<li>Object relational mapping</li>
<li>Dependency injection</li>
<li>Dynamic or weak type systems</li>
<li>Mutable domain model</li>
<li>Framework rather than library</li>
<li>Databinding & MVVM (since learning the <a href="/blog/2016/06/20/fsharp-elm-part1">Elm Architecture</a>)</li>
</ul>
<img style="border:1px solid black" src="/public/twitter/NoDI.png" title="No DI"/>
<p>In an attempt to reduce complexity, there is a trend of building systems out of batches of data transformation scripts or moving to microservices.
These don't reduce complexity; they dramatically increase it.
Distributed systems are harder to reason about and change. Doing this to be able to scale out can make sense but it has to be done with great skill.</p>
<h3><a name="Short-term-gain-long-term-pain" class="anchor" href="#Short-term-gain-long-term-pain">Short term gain, long term pain</a></h3>
<blockquote>
<p>Simplicity is prerequisite for reliability.<br />
<cite>Edsger W Dijkstra</cite></p>
</blockquote>
<p>Rich Hickey has a brilliant <a href="https://www.infoq.com/presentations/Simple-Made-Easy">presentation</a> explaining the difference between simple and easy in software development.
Every software developer needs to watch this. It is also very entertaining.</p>
<p>Some key takeaways are:</p>
<ul>
<li>Simplicity is a choice.</li>
<li>Ignoring complexity will slow you down over the long term. On throwaway or trivial projects, nothing much matters.</li>
<li>Information is simple. Don't ruin it by hiding it behind a micro-language.</li>
<li>Ability to reason about your program is critical.</li>
<li>Guard rail programming, i.e. tests rather than types, is not simple. It does not help you get to where you want to go.</li>
</ul>
<img style="border:1px solid black" src="/public/twitter/DevSpeed.png" title="Dev Speed"/>
<h3><a name="Performance-of-low-vs-high-level-languages" class="anchor" href="#Performance-of-low-vs-high-level-languages">Performance of low vs high level languages</a></h3>
<p>Let's say C can be 20%-50% faster than F# for a given algorithm. In my experience, getting to the best algorithm can produce an order of magnitude or more increase in performance.
I've seen systems that have become so complicated that even though a better algorithm is known it can't be used because it would require a large rewrite.
The complexity limit had been reached. This is particularly true of many C++ quant libraries.</p>
<p>Using a high level functional language with ten times less code provides simplicity to explore better algorithms and use generic performance techniques such as asynchronous programming and memoization.</p>
<a href="http://flyingfrogblog.blogspot.com/2015/11/c-vs-c-performance-deleted.html"><img style="border:1px solid black" src="/public/perf/cpp.png" title="C++ vs C# - Jon Harrop"/></a>
<p>Performance is complicated. It is often more about the movement of data than the calculation itself.
I prefer to start in the highest level language (F#) and move an algorithm to the lowest level language (C) as a last resort.
How often do I need to do this? Very rarely. Really only for access to chip optimised linear algebra and optimisation libraries.</p>
<h2><a name="Why-F" class="anchor" href="#Why-F">Why F#?</a></h2>
<p>Functional programming is simple-first programming.</p>
<p>Why is functional programming so simple? Because it comes from mathematics as the simplest possible programming construct.
You don't have to understand category theory to benefit from this.</p>
<blockquote>
<p>Functional languages were <a href="https://www.youtube.com/watch?v=IOiZatlZtGU&t=27m52s">discovered</a>, not invented. Many of you work in languages that were invented. And it shows.<br />
<cite>Prof Philip Wadler</cite></p>
</blockquote>
<p>Functional programming is not a fad the profession can ignore. Its rigorous mathematical foundation means that it will be around forever.
Software developers should be encouraged to learn the benefits it provides.</p>
<h3><a name="Choose-data-type-safety-and-functions-over-objects" class="anchor" href="#Choose-data-type-safety-and-functions-over-objects">Choose data type safety and functions over objects</a></h3>
<p>Data is simple. This is especially true in a strong type system that supports union types. Illegal state can be made unrepresentable.</p>
<p>Pure functions are simple. They always give the same output for the same input. They are easy to reason about and test.</p>
<p>Objects are complex. They fuse data and functions with side effects. They hold links to other objects. They are hard to almost impossible to <a href="https://fsharpforfunandprofit.com/posts/is-your-language-unreasonable/">reason about</a>. Testing is painful.
Software developers have to become familiar with the codebase and hold a large model of the system in their head. Don't let them go on holiday.</p>
<img style="border:1px solid black" src="/public/twitter/UnionTypes.png" title="Union Types"/>
<h3><a name="Choose-immutability-over-the-mutant" class="anchor" href="#Choose-immutability-over-the-mutant">Choose immutability over the mutant</a></h3>
<p>How do you handle queries and calculations (possibly long running) on a mutable domain model? Concurrent collections? Cross domain locking?
What you have created is a bug paradise. They will get cosy and settle in for the long term.</p>
<p>In the domain I work in a number of statistics (risk attribution, backtesting, what if analysis) are about changing the state slightly and comparing the results of a calculation.
How would you do this in a mutable domain model? Locking and transactions? Clone the world? Visitor pattern? I've been there and wouldn't wish it on anyone.</p>
<img style="border:1px solid black" src="/public/twitter/testable.png" title="Testable"/>
<h3><a name="Choose-linear-composition-over-spaghetti" class="anchor" href="#Choose-linear-composition-over-spaghetti">Choose linear composition over spaghetti</a></h3>
<p>There is something missing between perfect data, pure functions and beautiful systems.</p>
<p>Functional programming allows functions to be passed around just like data. Functions can accept other functions as an input in a generic way. They are called higher-order functions.</p>
<p>This may sound alien but it provides a quantum leap in terms of code reuse and assembling systems.
In fact, I didn't understand the full power of code reuse until I started programming in a functional language.</p>
<img style="border:1px solid black" src="/public/twitter/OOP.png" title="OOP"/>
<p>Object orientated programming has a poorer method of assembling systems. Objects are given to other objects. Dependency injection has been invented to make this easier.
This helps but results in systems that are harder to reason about and increases complexity.</p>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p>We need to talk about object oriented programming. We have patterns to work around its deficiencies. These, taken to the limit, make it look more functional.
Every release of Java and C# add more functional features. Unfortunately, this will not ultimately fix these languages, it just increases their complexity.
As an industry we need to decide how we should move on.</p>
<a href="https://twitter.com/antonmry/status/1247245202831609863?s=20"><img style="border:1px solid black" src="/public/twitter/python2.png" title="Python"/></a>
<p>For a one off, short term, or simple project you can handle additional complexity in exchange for some quick productivity gains. This is what Python, Rails, R etc are great at.</p>
<p>For long term critical systems that evolve over time the focus must be on simplicity. The programming language and tools need to help us do this. This is why I code in F#.</p>
<img style="border:1px solid black" src="/public/twitter/complexity_ship.png" title="Complexity Ship"/>
F# Implementation of The Elm Architecture - Part 2
2016-07-01T00:00:00+01:00
http://anthonylloyd.github.io/blog/2016/07/01/fsharp-elm-part2
<p>This is the second part of a prototype of <a href="http://guide.elm-lang.org/architecture/index.html">The Elm Architecture</a> in F#.
The first <a href="/blog/2016/06/20/fsharp-elm-part1">post</a> covered the logical UI and this covers using it with WPF and Xamarin.</p>
<h2><a name="Native-UI-Implementation" class="anchor" href="#Native-UI-Implementation">Native UI Implementation</a></h2>
<p>Below is the WPF implementation of the <code>INativeUI</code> interface.
The Xamarin <a href="https://github.com/AnthonyLloyd/Elm/blob/master/XamarinApp/Xamarin.fs">implementation</a> is very similar.</p>
<p>Threading proves to be simple with all the updates being performed on the UI thread in a single call.
<code>UIUpdate</code> maps well to the operations required to locate and update the native UI elements.</p>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="k">module</span> <span onmouseout="hideTip(event, 'fs196', 771)" onmouseover="showTip(event, 'fs196', 771)" class="m">WPF</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs197', 772)" onmouseover="showTip(event, 'fs197', 772)" class="fn">CreateNaiveUI</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs198', 773)" onmouseover="showTip(event, 'fs198', 773)" class="id">root</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs199', 774)" onmouseover="showTip(event, 'fs199', 774)" class="rt">ContentControl</span><span class="pn">)</span> <span class="o">=</span>
<span class="k">let</span> <span class="k">rec</span> <span onmouseout="hideTip(event, 'fs200', 775)" onmouseover="showTip(event, 'fs200', 775)" class="fn">createUI</span> <span onmouseout="hideTip(event, 'fs127', 776)" onmouseover="showTip(event, 'fs127', 776)" class="id">ui</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs201', 777)" onmouseover="showTip(event, 'fs201', 777)" class="rt">UIElement</span> <span class="o">=</span>
<span class="k">match</span> <span onmouseout="hideTip(event, 'fs127', 778)" onmouseover="showTip(event, 'fs127', 778)" class="id">ui</span> <span class="k">with</span>
<span class="pn">|</span><span onmouseout="hideTip(event, 'fs38', 779)" onmouseover="showTip(event, 'fs38', 779)" class="uc">Text</span> <span onmouseout="hideTip(event, 'fs75', 780)" onmouseover="showTip(event, 'fs75', 780)" class="id">text</span> <span class="k">-></span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs202', 781)" onmouseover="showTip(event, 'fs202', 781)" class="id">c</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs203', 782)" onmouseover="showTip(event, 'fs203', 782)" class="rt">Label</span><span class="pn">(</span><span class="id">Content</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs39', 783)" onmouseover="showTip(event, 'fs39', 783)" class="fn">string</span> <span onmouseout="hideTip(event, 'fs75', 784)" onmouseover="showTip(event, 'fs75', 784)" class="id">text</span><span class="pn">)</span>
<span class="k">upcast</span> <span onmouseout="hideTip(event, 'fs202', 785)" onmouseover="showTip(event, 'fs202', 785)" class="id">c</span>
<span class="pn">|</span><span onmouseout="hideTip(event, 'fs204', 786)" onmouseover="showTip(event, 'fs204', 786)" class="uc">Input</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs75', 787)" onmouseover="showTip(event, 'fs75', 787)" class="id">text</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs205', 788)" onmouseover="showTip(event, 'fs205', 788)" class="mv">event</span><span class="pn">)</span> <span class="k">-></span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs206', 789)" onmouseover="showTip(event, 'fs206', 789)" class="id">c</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs207', 790)" onmouseover="showTip(event, 'fs207', 790)" class="rt">TextBox</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs38', 791)" onmouseover="showTip(event, 'fs38', 791)" class="id">Text</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs39', 792)" onmouseover="showTip(event, 'fs39', 792)" class="fn">string</span> <span onmouseout="hideTip(event, 'fs75', 793)" onmouseover="showTip(event, 'fs75', 793)" class="id">text</span><span class="pn">)</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs208', 794)" onmouseover="showTip(event, 'fs208', 794)" class="mv">event</span> <span class="o">=</span> <span class="o">!</span><span onmouseout="hideTip(event, 'fs208', 795)" onmouseover="showTip(event, 'fs208', 795)" class="mv">event</span>
<span onmouseout="hideTip(event, 'fs206', 796)" onmouseover="showTip(event, 'fs206', 796)" class="fn">c</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs209', 797)" onmouseover="showTip(event, 'fs209', 797)" class="id">TextChanged</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs210', 798)" onmouseover="showTip(event, 'fs210', 798)" class="id">Add</span><span class="pn">(</span><span class="k">fun</span> <span class="id">_</span> <span class="k">-></span> <span class="k">let</span> <span onmouseout="hideTip(event, 'fs211', 799)" onmouseover="showTip(event, 'fs211', 799)" class="id">t</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs206', 800)" onmouseover="showTip(event, 'fs206', 800)" class="id">c</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs212', 801)" onmouseover="showTip(event, 'fs212', 801)" class="id">Text</span>
<span onmouseout="hideTip(event, 'fs213', 802)" onmouseover="showTip(event, 'fs213', 802)" class="k">async</span> <span class="pn">{</span> <span class="o">!</span><span onmouseout="hideTip(event, 'fs208', 803)" onmouseover="showTip(event, 'fs208', 803)" class="mv">event</span> <span onmouseout="hideTip(event, 'fs211', 804)" onmouseover="showTip(event, 'fs211', 804)" class="id">t</span> <span class="pn">}</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs214', 805)" onmouseover="showTip(event, 'fs214', 805)" class="rt">Async</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs215', 806)" onmouseover="showTip(event, 'fs215', 806)" class="id">Start</span><span class="pn">)</span>
<span class="k">upcast</span> <span onmouseout="hideTip(event, 'fs206', 807)" onmouseover="showTip(event, 'fs206', 807)" class="id">c</span>
<span class="pn">|</span><span onmouseout="hideTip(event, 'fs216', 808)" onmouseover="showTip(event, 'fs216', 808)" class="uc">Button</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs75', 809)" onmouseover="showTip(event, 'fs75', 809)" class="id">text</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs217', 810)" onmouseover="showTip(event, 'fs217', 810)" class="mv">event</span><span class="pn">)</span> <span class="k">-></span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs218', 811)" onmouseover="showTip(event, 'fs218', 811)" class="id">c</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs216', 812)" onmouseover="showTip(event, 'fs216', 812)" class="rt">Button</span><span class="pn">(</span><span class="id">Content</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs39', 813)" onmouseover="showTip(event, 'fs39', 813)" class="fn">string</span> <span onmouseout="hideTip(event, 'fs75', 814)" onmouseover="showTip(event, 'fs75', 814)" class="id">text</span><span class="pn">)</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs219', 815)" onmouseover="showTip(event, 'fs219', 815)" class="mv">event</span> <span class="o">=</span> <span class="o">!</span><span onmouseout="hideTip(event, 'fs219', 816)" onmouseover="showTip(event, 'fs219', 816)" class="mv">event</span>
<span onmouseout="hideTip(event, 'fs218', 817)" onmouseover="showTip(event, 'fs218', 817)" class="fn">c</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs220', 818)" onmouseover="showTip(event, 'fs220', 818)" class="id">Click</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs210', 819)" onmouseover="showTip(event, 'fs210', 819)" class="id">Add</span><span class="pn">(</span><span class="k">fun</span> <span class="id">_</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs213', 820)" onmouseover="showTip(event, 'fs213', 820)" class="k">async</span> <span class="pn">{</span> <span class="pn">(</span><span class="o">!</span><span onmouseout="hideTip(event, 'fs219', 821)" onmouseover="showTip(event, 'fs219', 821)" class="mv">event</span><span class="pn">)</span><span class="pn">(</span><span class="pn">)</span> <span class="pn">}</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs214', 822)" onmouseover="showTip(event, 'fs214', 822)" class="rt">Async</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs215', 823)" onmouseover="showTip(event, 'fs215', 823)" class="id">Start</span><span class="pn">)</span>
<span class="k">upcast</span> <span onmouseout="hideTip(event, 'fs218', 824)" onmouseover="showTip(event, 'fs218', 824)" class="id">c</span>
<span class="pn">|</span><span onmouseout="hideTip(event, 'fs42', 825)" onmouseover="showTip(event, 'fs42', 825)" class="uc">Div</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs89', 826)" onmouseover="showTip(event, 'fs89', 826)" class="id">layout</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs221', 827)" onmouseover="showTip(event, 'fs221', 827)" class="id">list</span><span class="pn">)</span> <span class="k">-></span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs222', 828)" onmouseover="showTip(event, 'fs222', 828)" class="id">children</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs91', 829)" onmouseover="showTip(event, 'fs91', 829)" class="m">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs92', 830)" onmouseover="showTip(event, 'fs92', 830)" class="id">map</span> <span onmouseout="hideTip(event, 'fs200', 831)" onmouseover="showTip(event, 'fs200', 831)" class="fn">createUI</span> <span onmouseout="hideTip(event, 'fs221', 832)" onmouseover="showTip(event, 'fs221', 832)" class="id">list</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs223', 833)" onmouseover="showTip(event, 'fs223', 833)" class="id">c</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs224', 834)" onmouseover="showTip(event, 'fs224', 834)" class="rt">StackPanel</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs225', 835)" onmouseover="showTip(event, 'fs225', 835)" class="id">Orientation</span><span class="o">=</span>
<span class="k">match</span> <span onmouseout="hideTip(event, 'fs89', 836)" onmouseover="showTip(event, 'fs89', 836)" class="id">layout</span> <span class="k">with</span>
<span class="pn">|</span><span onmouseout="hideTip(event, 'fs36', 837)" onmouseover="showTip(event, 'fs36', 837)" class="uc">Vertical</span><span class="k">-></span><span onmouseout="hideTip(event, 'fs225', 838)" onmouseover="showTip(event, 'fs225', 838)" class="vt">Orientation</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs226', 839)" onmouseover="showTip(event, 'fs226', 839)" class="id">Vertical</span>
<span class="pn">|</span><span onmouseout="hideTip(event, 'fs35', 840)" onmouseover="showTip(event, 'fs35', 840)" class="uc">Horizontal</span><span class="k">-></span><span onmouseout="hideTip(event, 'fs225', 841)" onmouseover="showTip(event, 'fs225', 841)" class="vt">Orientation</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs227', 842)" onmouseover="showTip(event, 'fs227', 842)" class="id">Horizontal</span><span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs91', 843)" onmouseover="showTip(event, 'fs91', 843)" class="m">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs96', 844)" onmouseover="showTip(event, 'fs96', 844)" class="id">iter</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs223', 845)" onmouseover="showTip(event, 'fs223', 845)" class="fn">c</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs228', 846)" onmouseover="showTip(event, 'fs228', 846)" class="id">Children</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs229', 847)" onmouseover="showTip(event, 'fs229', 847)" class="id">Add</span><span class="o">></span><span class="pn">></span><span onmouseout="hideTip(event, 'fs76', 848)" onmouseover="showTip(event, 'fs76', 848)" class="fn">ignore</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs222', 849)" onmouseover="showTip(event, 'fs222', 849)" class="id">children</span>
<span class="k">upcast</span> <span onmouseout="hideTip(event, 'fs223', 850)" onmouseover="showTip(event, 'fs223', 850)" class="id">c</span>
<span class="k">let</span> <span class="k">rec</span> <span onmouseout="hideTip(event, 'fs230', 851)" onmouseover="showTip(event, 'fs230', 851)" class="fn">locatePanel</span> <span onmouseout="hideTip(event, 'fs231', 852)" onmouseover="showTip(event, 'fs231', 852)" class="id">loc</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs232', 853)" onmouseover="showTip(event, 'fs232', 853)" class="rt">Panel</span> <span class="o">=</span>
<span class="k">match</span> <span onmouseout="hideTip(event, 'fs231', 854)" onmouseover="showTip(event, 'fs231', 854)" class="id">loc</span> <span class="k">with</span>
<span class="pn">|</span><span class="pn">[</span><span class="pn">]</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs198', 855)" onmouseover="showTip(event, 'fs198', 855)" class="id">root</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs233', 856)" onmouseover="showTip(event, 'fs233', 856)" class="id">Content</span> <span class="o">:?></span> <span class="id">_</span>
<span class="pn">|</span><span onmouseout="hideTip(event, 'fs15', 857)" onmouseover="showTip(event, 'fs15', 857)" class="id">i</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs234', 858)" onmouseover="showTip(event, 'fs234', 858)" class="id">xs</span> <span class="k">-></span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs230', 859)" onmouseover="showTip(event, 'fs230', 859)" class="fn">locatePanel</span> <span onmouseout="hideTip(event, 'fs234', 860)" onmouseover="showTip(event, 'fs234', 860)" class="id">xs</span><span class="pn">)</span><span class="pn">.</span><span class="id">Children</span><span class="pn">.</span><span class="id">Item</span> <span onmouseout="hideTip(event, 'fs15', 861)" onmouseover="showTip(event, 'fs15', 861)" class="id">i</span> <span class="o">:?></span> <span class="id">_</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs235', 862)" onmouseover="showTip(event, 'fs235', 862)" class="fn">uiUpdate</span> <span onmouseout="hideTip(event, 'fs236', 863)" onmouseover="showTip(event, 'fs236', 863)" class="id">u</span> <span class="o">=</span>
<span class="k">match</span> <span onmouseout="hideTip(event, 'fs236', 864)" onmouseover="showTip(event, 'fs236', 864)" class="id">u</span> <span class="k">with</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs45', 865)" onmouseover="showTip(event, 'fs45', 865)" class="uc">InsertUI</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs231', 866)" onmouseover="showTip(event, 'fs231', 866)" class="id">loc</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs127', 867)" onmouseover="showTip(event, 'fs127', 867)" class="id">ui</span><span class="pn">)</span> <span class="k">-></span>
<span class="k">match</span> <span onmouseout="hideTip(event, 'fs231', 868)" onmouseover="showTip(event, 'fs231', 868)" class="id">loc</span> <span class="k">with</span>
<span class="pn">|</span><span class="pn">[</span><span class="pn">]</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs198', 869)" onmouseover="showTip(event, 'fs198', 869)" class="id">root</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs233', 870)" onmouseover="showTip(event, 'fs233', 870)" class="id">Content</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs200', 871)" onmouseover="showTip(event, 'fs200', 871)" class="fn">createUI</span> <span onmouseout="hideTip(event, 'fs127', 872)" onmouseover="showTip(event, 'fs127', 872)" class="id">ui</span>
<span class="pn">|</span><span onmouseout="hideTip(event, 'fs15', 873)" onmouseover="showTip(event, 'fs15', 873)" class="id">i</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs234', 874)" onmouseover="showTip(event, 'fs234', 874)" class="id">xs</span> <span class="k">-></span> <span class="fn">(</span><span onmouseout="hideTip(event, 'fs230', 875)" onmouseover="showTip(event, 'fs230', 875)" class="fn">locatePanel</span> <span onmouseout="hideTip(event, 'fs234', 876)" onmouseover="showTip(event, 'fs234', 876)" class="id">xs</span><span class="pn">)</span><span class="pn">.</span><span class="id">Children</span><span class="pn">.</span><span class="id">Insert</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs15', 877)" onmouseover="showTip(event, 'fs15', 877)" class="id">i</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs200', 878)" onmouseover="showTip(event, 'fs200', 878)" class="fn">createUI</span> <span onmouseout="hideTip(event, 'fs127', 879)" onmouseover="showTip(event, 'fs127', 879)" class="id">ui</span><span class="pn">)</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs47', 880)" onmouseover="showTip(event, 'fs47', 880)" class="uc">UpdateUI</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs231', 881)" onmouseover="showTip(event, 'fs231', 881)" class="id">loc</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs127', 882)" onmouseover="showTip(event, 'fs127', 882)" class="id">ui</span><span class="pn">)</span> <span class="k">-></span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs237', 883)" onmouseover="showTip(event, 'fs237', 883)" class="id">element</span> <span class="o">=</span> <span class="k">match</span> <span onmouseout="hideTip(event, 'fs231', 884)" onmouseover="showTip(event, 'fs231', 884)" class="id">loc</span> <span class="k">with</span>
<span class="pn">|</span><span class="pn">[</span><span class="pn">]</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs198', 885)" onmouseover="showTip(event, 'fs198', 885)" class="id">root</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs233', 886)" onmouseover="showTip(event, 'fs233', 886)" class="id">Content</span> <span class="o">:?></span> <span class="id">_</span>
<span class="pn">|</span><span onmouseout="hideTip(event, 'fs15', 887)" onmouseover="showTip(event, 'fs15', 887)" class="id">i</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs234', 888)" onmouseover="showTip(event, 'fs234', 888)" class="id">xs</span> <span class="k">-></span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs230', 889)" onmouseover="showTip(event, 'fs230', 889)" class="fn">locatePanel</span> <span onmouseout="hideTip(event, 'fs234', 890)" onmouseover="showTip(event, 'fs234', 890)" class="id">xs</span><span class="pn">)</span><span class="pn">.</span><span class="id">Children</span><span class="pn">.</span><span class="id">Item</span> <span onmouseout="hideTip(event, 'fs15', 891)" onmouseover="showTip(event, 'fs15', 891)" class="id">i</span>
<span class="k">match</span> <span onmouseout="hideTip(event, 'fs127', 892)" onmouseover="showTip(event, 'fs127', 892)" class="id">ui</span> <span class="k">with</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs38', 893)" onmouseover="showTip(event, 'fs38', 893)" class="uc">Text</span> <span onmouseout="hideTip(event, 'fs75', 894)" onmouseover="showTip(event, 'fs75', 894)" class="id">text</span> <span class="k">-></span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs237', 895)" onmouseover="showTip(event, 'fs237', 895)" class="id">element</span> <span class="o">:?></span> <span onmouseout="hideTip(event, 'fs203', 896)" onmouseover="showTip(event, 'fs203', 896)" class="rt">Label</span><span class="pn">)</span><span class="pn">.</span><span class="id">Content</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs39', 897)" onmouseover="showTip(event, 'fs39', 897)" class="fn">string</span> <span onmouseout="hideTip(event, 'fs75', 898)" onmouseover="showTip(event, 'fs75', 898)" class="id">text</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs204', 899)" onmouseover="showTip(event, 'fs204', 899)" class="uc">Input</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs75', 900)" onmouseover="showTip(event, 'fs75', 900)" class="id">text</span><span class="pn">,</span><span class="id">_</span><span class="pn">)</span> <span class="k">-></span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs237', 901)" onmouseover="showTip(event, 'fs237', 901)" class="id">element</span> <span class="o">:?></span> <span onmouseout="hideTip(event, 'fs207', 902)" onmouseover="showTip(event, 'fs207', 902)" class="rt">TextBox</span><span class="pn">)</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs38', 903)" onmouseover="showTip(event, 'fs38', 903)" class="id">Text</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs39', 904)" onmouseover="showTip(event, 'fs39', 904)" class="fn">string</span> <span onmouseout="hideTip(event, 'fs75', 905)" onmouseover="showTip(event, 'fs75', 905)" class="id">text</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs216', 906)" onmouseover="showTip(event, 'fs216', 906)" class="uc">Button</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs75', 907)" onmouseover="showTip(event, 'fs75', 907)" class="id">text</span><span class="pn">,</span><span class="id">_</span><span class="pn">)</span> <span class="k">-></span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs237', 908)" onmouseover="showTip(event, 'fs237', 908)" class="id">element</span> <span class="o">:?></span> <span onmouseout="hideTip(event, 'fs216', 909)" onmouseover="showTip(event, 'fs216', 909)" class="rt">Button</span><span class="pn">)</span><span class="pn">.</span><span class="id">Content</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs39', 910)" onmouseover="showTip(event, 'fs39', 910)" class="fn">string</span> <span onmouseout="hideTip(event, 'fs75', 911)" onmouseover="showTip(event, 'fs75', 911)" class="id">text</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs42', 912)" onmouseover="showTip(event, 'fs42', 912)" class="uc">Div</span> <span class="id">_</span> <span class="k">-></span> <span class="pn">(</span><span class="pn">)</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs48', 913)" onmouseover="showTip(event, 'fs48', 913)" class="uc">ReplaceUI</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs231', 914)" onmouseover="showTip(event, 'fs231', 914)" class="id">loc</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs127', 915)" onmouseover="showTip(event, 'fs127', 915)" class="id">ui</span><span class="pn">)</span> <span class="k">-></span>
<span class="k">match</span> <span onmouseout="hideTip(event, 'fs231', 916)" onmouseover="showTip(event, 'fs231', 916)" class="id">loc</span> <span class="k">with</span>
<span class="pn">|</span><span class="pn">[</span><span class="pn">]</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs198', 917)" onmouseover="showTip(event, 'fs198', 917)" class="id">root</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs233', 918)" onmouseover="showTip(event, 'fs233', 918)" class="id">Content</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs200', 919)" onmouseover="showTip(event, 'fs200', 919)" class="fn">createUI</span> <span onmouseout="hideTip(event, 'fs127', 920)" onmouseover="showTip(event, 'fs127', 920)" class="id">ui</span>
<span class="pn">|</span><span onmouseout="hideTip(event, 'fs15', 921)" onmouseover="showTip(event, 'fs15', 921)" class="id">i</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs234', 922)" onmouseover="showTip(event, 'fs234', 922)" class="id">xs</span> <span class="k">-></span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs238', 923)" onmouseover="showTip(event, 'fs238', 923)" class="id">c</span> <span class="o">=</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs230', 924)" onmouseover="showTip(event, 'fs230', 924)" class="fn">locatePanel</span> <span onmouseout="hideTip(event, 'fs234', 925)" onmouseover="showTip(event, 'fs234', 925)" class="id">xs</span><span class="pn">)</span><span class="pn">.</span><span class="id">Children</span>
<span onmouseout="hideTip(event, 'fs238', 926)" onmouseover="showTip(event, 'fs238', 926)" class="fn">c</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs239', 927)" onmouseover="showTip(event, 'fs239', 927)" class="id">RemoveAt</span> <span onmouseout="hideTip(event, 'fs15', 928)" onmouseover="showTip(event, 'fs15', 928)" class="id">i</span>
<span onmouseout="hideTip(event, 'fs238', 929)" onmouseover="showTip(event, 'fs238', 929)" class="fn">c</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs240', 930)" onmouseover="showTip(event, 'fs240', 930)" class="id">Insert</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs15', 931)" onmouseover="showTip(event, 'fs15', 931)" class="id">i</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs200', 932)" onmouseover="showTip(event, 'fs200', 932)" class="fn">createUI</span> <span onmouseout="hideTip(event, 'fs127', 933)" onmouseover="showTip(event, 'fs127', 933)" class="id">ui</span><span class="pn">)</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs49', 934)" onmouseover="showTip(event, 'fs49', 934)" class="uc">RemoveUI</span> <span onmouseout="hideTip(event, 'fs231', 935)" onmouseover="showTip(event, 'fs231', 935)" class="id">loc</span> <span class="k">-></span>
<span class="k">match</span> <span onmouseout="hideTip(event, 'fs231', 936)" onmouseover="showTip(event, 'fs231', 936)" class="id">loc</span> <span class="k">with</span>
<span class="pn">|</span><span class="pn">[</span><span class="pn">]</span> <span class="k">-></span> <span class="pn">(</span><span class="pn">)</span>
<span class="pn">|</span><span onmouseout="hideTip(event, 'fs15', 937)" onmouseover="showTip(event, 'fs15', 937)" class="id">i</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs234', 938)" onmouseover="showTip(event, 'fs234', 938)" class="id">xs</span> <span class="k">-></span> <span class="fn">(</span><span onmouseout="hideTip(event, 'fs230', 939)" onmouseover="showTip(event, 'fs230', 939)" class="fn">locatePanel</span> <span onmouseout="hideTip(event, 'fs234', 940)" onmouseover="showTip(event, 'fs234', 940)" class="id">xs</span><span class="pn">)</span><span class="pn">.</span><span class="id">Children</span><span class="pn">.</span><span class="id">RemoveAt</span> <span onmouseout="hideTip(event, 'fs15', 941)" onmouseover="showTip(event, 'fs15', 941)" class="id">i</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs50', 942)" onmouseover="showTip(event, 'fs50', 942)" class="uc">EventUI</span> <span class="id">_</span> <span class="k">-></span> <span class="pn">(</span><span class="pn">)</span>
<span class="pn">{</span> <span class="k">new</span> <span onmouseout="hideTip(event, 'fs58', 943)" onmouseover="showTip(event, 'fs58', 943)" class="if">INativeUI</span> <span class="k">with</span>
<span class="k">member</span> <span class="id">__</span><span class="pn">.</span><span class="fn">Send</span> <span onmouseout="hideTip(event, 'fs241', 944)" onmouseover="showTip(event, 'fs241', 944)" class="id">list</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs198', 945)" onmouseover="showTip(event, 'fs198', 945)" class="fn">root</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs242', 946)" onmouseover="showTip(event, 'fs242', 946)" class="id">Dispatcher</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs243', 947)" onmouseover="showTip(event, 'fs243', 947)" class="id">Invoke</span> <span class="pn">(</span><span class="k">fun</span> <span class="pn">(</span><span class="pn">)</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs91', 948)" onmouseover="showTip(event, 'fs91', 948)" class="m">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs96', 949)" onmouseover="showTip(event, 'fs96', 949)" class="id">iter</span> <span onmouseout="hideTip(event, 'fs235', 950)" onmouseover="showTip(event, 'fs235', 950)" class="fn">uiUpdate</span> <span onmouseout="hideTip(event, 'fs241', 951)" onmouseover="showTip(event, 'fs241', 951)" class="id">list</span><span class="pn">)</span>
<span class="pn">}</span>
</code></pre>
<h2><a name="Results" class="anchor" href="#Results">Results</a></h2>
<p>The same <code>CounterList</code> UI application from the previous <a href="/blog/2016/06/20/fsharp-elm-part1">post</a> has been used across a number of native UIs.
The example <a href="https://github.com/AnthonyLloyd/Elm">source</a> produces the following mobile and desktop UIs.</p>
<table style="border:0px">
<tr>
<td style="background-color:white;border:0px"><img src="/public/elm/WinPhone80.png" title="Mobile Application" width="340px" height="500px"/></td>
<td style="background-color:white;border:0px"><img src="/public/elm/WinWPF.png" title="WPF Application" width="340px" height="500px"/></td>
</tr>
<tr>
<td style="background-color:white;border:0px"><img src="/public/elm/WinApp.png" title="Metro Application" width="340px" height="500px"/></td>
<td style="background-color:white;border:0px"><img src="/public/elm/WinUWP.png" title="UWP Application" width="340px" height="500px"/></td>
</tr>
</table>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p><a href="http://guide.elm-lang.org/architecture/index.html">The Elm Architecture</a> continues to look to be a very promising pattern.</p>
<p>The <code>INativeUI</code> implementation is a single place for native UI element creation and is much more DRY than other UI models.
So far, styling has not been considered, but this single place should make it easier with both an Elm and a CSS model being possible.</p>
<p><a href="http://guide.elm-lang.org/architecture/index.html">The Elm Architecture</a> moves the view and event logic away from the native UI.
This has the benefit of making the UI more testable and at the same time making migration of the native UI easier.</p>
<p>These benefits, combined with the type safety and composability outlined in the previous <a href="/blog/2016/06/20/fsharp-elm-part1">post</a>, make this pattern compelling.</p>
<p>UPDATED:</p>
<img style="border:1px solid black" src="/public/elm/donpraise.png"/>
<div class="tip" id="fs1">Multiple items<br />module List<br /><br />from Microsoft.FSharp.Collections<br /><br />--------------------<br />type List<'T> =<br />  | ( [] )<br />  | ( :: ) of Head: 'T * Tail: 'T list<br />    interface IReadOnlyList<'T><br />    interface IReadOnlyCollection<'T><br />    interface IEnumerable<br />    interface IEnumerable<'T><br />    member GetSlice : startIndex:int option * endIndex:int option -> 'T list<br />    member Head : 'T<br />    member IsEmpty : bool<br />    member Item : index:int -> 'T with get<br />    member Length : int<br />    member Tail : 'T list<br />    ...</div>
<div class="tip" id="fs2">val remove : n:int -> l:'a list -> 'a list * 'a list</div>
<div class="tip" id="fs3">val n : int</div>
<div class="tip" id="fs4">val l : 'a list</div>
<div class="tip" id="fs5">val pop : (int -> 'b list -> 'b list -> 'b list * 'b list)</div>
<div class="tip" id="fs6">val l : 'b list</div>
<div class="tip" id="fs7">val p : 'b list</div>
<div class="tip" id="fs8">val x : 'b</div>
<div class="tip" id="fs9">val xs : 'b list</div>
<div class="tip" id="fs10">val add : p:'a list -> l:'a list -> 'a list</div>
<div class="tip" id="fs11">val p : 'a list</div>
<div class="tip" id="fs12">val x : 'a</div>
<div class="tip" id="fs13">val xs : 'a list</div>
<div class="tip" id="fs14">val removei : i:int -> l:'a list -> 'a list</div>
<div class="tip" id="fs15">val i : int</div>
<div class="tip" id="fs16">val t : 'a list</div>
<div class="tip" id="fs17">val tail : list:'T list -> 'T list</div>
<div class="tip" id="fs18">val replacei : i:int -> item:'a -> l:'a list -> 'a list</div>
<div class="tip" id="fs19">val item : 'a</div>
<div class="tip" id="fs20">val revMap : mapping:('a -> 'b) -> list:'a list -> 'b list</div>
<div class="tip" id="fs21">val mapping : ('a -> 'b)</div>
<div class="tip" id="fs22">Multiple items<br />val list : 'a list<br /><br />--------------------<br />type 'T list = List<'T></div>
<div class="tip" id="fs23">val work : ('a list -> 'b list -> 'b list)</div>
<div class="tip" id="fs24">val input : 'a list</div>
<div class="tip" id="fs25">val output : 'b list</div>
<div class="tip" id="fs26">val mapAt : i:int -> mapping:('a -> 'a) -> list:'a list -> 'a list</div>
<div class="tip" id="fs27">val mapping : ('a -> 'a)</div>
<div class="tip" id="fs28">val removed : 'a list</div>
<div class="tip" id="fs29">val tail : 'a list</div>
<div class="tip" id="fs30">val head : list:'T list -> 'T</div>
<div class="tip" id="fs31">Multiple items<br />module Event<br /><br />from Microsoft.FSharp.Control<br /><br />--------------------<br />type 'msg Event = ('msg -> unit) ref ref<br /><em><br /><br /> Message event used on the primative UI components.</em><br /><br />--------------------<br />type Event<'Delegate,'Args (requires delegate and 'Delegate :> Delegate)> =<br />  new : unit -> Event<'Delegate,'Args><br />  member Trigger : sender:obj * args:'Args -> unit<br />  member Publish : IEvent<'Delegate,'Args><br /><br />--------------------<br />new : unit -> Event<'Delegate,'Args></div>
<div class="tip" id="fs32">type unit = Unit</div>
<div class="tip" id="fs33">Multiple items<br />val ref : value:'T -> 'T ref<br /><br />--------------------<br />type 'T ref = Ref<'T></div>
<div class="tip" id="fs34">type Layout =<br />  | Horizontal<br />  | Vertical<br /><em><br /><br /> Layout for a section of UI components.</em></div>
<div class="tip" id="fs35">union case Layout.Horizontal: Layout</div>
<div class="tip" id="fs36">union case Layout.Vertical: Layout</div>
<div class="tip" id="fs37">type UI =<br />  | Text of string<br />  | Input of string * string Event<br />  | Button of string * unit Event<br />  | Div of Layout * UI list<br /><em><br /><br /> Primative UI components.</em></div>
<div class="tip" id="fs38">union case UI.Text: string -> UI</div>
<div class="tip" id="fs39">Multiple items<br />val string : value:'T -> string<br /><br />--------------------<br />type string = System.String</div>
<div class="tip" id="fs40">union case UI.Input: string * string Event -> UI</div>
<div class="tip" id="fs41">union case UI.Button: string * unit Event -> UI</div>
<div class="tip" id="fs42">union case UI.Div: Layout * UI list -> UI</div>
<div class="tip" id="fs43">type 'T list = List<'T></div>
<div class="tip" id="fs44">type UIUpdate =<br />  | InsertUI of int list * UI<br />  | UpdateUI of int list * UI<br />  | ReplaceUI of int list * UI<br />  | RemoveUI of int list<br />  | EventUI of (unit -> unit)<br /><em><br /><br /> UI component update and event redirection.</em></div>
<div class="tip" id="fs45">union case UIUpdate.InsertUI: int list * UI -> UIUpdate</div>
<div class="tip" id="fs46">Multiple items<br />val int : value:'T -> int (requires member op_Explicit)<br /><br />--------------------<br />type int = int32<br /><br />--------------------<br />type int<'Measure> = int</div>
<div class="tip" id="fs47">union case UIUpdate.UpdateUI: int list * UI -> UIUpdate</div>
<div class="tip" id="fs48">union case UIUpdate.ReplaceUI: int list * UI -> UIUpdate</div>
<div class="tip" id="fs49">union case UIUpdate.RemoveUI: int list -> UIUpdate</div>
<div class="tip" id="fs50">union case UIUpdate.EventUI: (unit -> unit) -> UIUpdate</div>
<div class="tip" id="fs51">Multiple items<br />type UI =<br />  | Text of string<br />  | Input of string * string Event<br />  | Button of string * unit Event<br />  | Div of Layout * UI list<br /><em><br /><br /> Primative UI components.</em><br /><br />--------------------<br />type 'msg UI =<br />  {UI: UI;<br />   mutable Event: 'msg -> unit;}<br /><em><br /><br /> UI component including a message event.</em></div>
<div class="tip" id="fs52">Multiple items<br />UI.UI: UI<br /><br />--------------------<br />type UI =<br />  | Text of string<br />  | Input of string * string Event<br />  | Button of string * unit Event<br />  | Div of Layout * UI list<br /><em><br /><br /> Primative UI components.</em><br /><br />--------------------<br />type 'msg UI =<br />  {UI: UI;<br />   mutable Event: 'msg -> unit;}<br /><em><br /><br /> UI component including a message event.</em></div>
<div class="tip" id="fs53">Multiple items<br />UI.Event: 'msg -> unit<br /><br />--------------------<br />module Event<br /><br />from Microsoft.FSharp.Control<br /><br />--------------------<br />type 'msg Event = ('msg -> unit) ref ref<br /><em><br /><br /> Message event used on the primative UI components.</em><br /><br />--------------------<br />type Event<'Delegate,'Args (requires delegate and 'Delegate :> Delegate)> =<br />  new : unit -> Event<'Delegate,'Args><br />  member Trigger : sender:obj * args:'Args -> unit<br />  member Publish : IEvent<'Delegate,'Args><br /><br />--------------------<br />new : unit -> Event<'Delegate,'Args></div>
<div class="tip" id="fs54">type App<'msg,'model> =<br />  {Model: 'model;<br />   Update: 'msg -> 'model -> 'model;<br />   View: 'model -> 'msg UI;}<br /><em><br /><br /> UI application.</em></div>
<div class="tip" id="fs55">App.Model: 'model</div>
<div class="tip" id="fs56">App.Update: 'msg -> 'model -> 'model</div>
<div class="tip" id="fs57">App.View: 'model -> 'msg UI</div>
<div class="tip" id="fs58">type INativeUI =<br />  interface<br />    abstract member Send : UIUpdate list -> unit<br />  end<br /><em><br /><br /> Native UI interface.</em></div>
<div class="tip" id="fs59">Multiple items<br />type CompilationRepresentationAttribute =<br />  inherit Attribute<br />  new : flags:CompilationRepresentationFlags -> CompilationRepresentationAttribute<br />  member Flags : CompilationRepresentationFlags<br /><br />--------------------<br />new : flags:CompilationRepresentationFlags -> CompilationRepresentationAttribute</div>
<div class="tip" id="fs60">type CompilationRepresentationFlags =<br />  | None = 0<br />  | Static = 1<br />  | Instance = 2<br />  | ModuleSuffix = 4<br />  | UseNullAsTrueValue = 8<br />  | Event = 16</div>
<div class="tip" id="fs61">CompilationRepresentationFlags.ModuleSuffix: CompilationRepresentationFlags = 4</div>
<div class="tip" id="fs62">val memoize<'model,'msg (requires reference type and reference type)> : (('model -> 'msg UI) -> 'model -> 'msg UI) (requires reference type and reference type)<br /><em><br /><br /> Memoize view generation from model object references.</em></div>
<div class="tip" id="fs63">val not : value:bool -> bool</div>
<div class="tip" id="fs64">val d : System.Runtime.CompilerServices.ConditionalWeakTable<'model,'msg UI> (requires reference type and reference type)</div>
<div class="tip" id="fs65">namespace System</div>
<div class="tip" id="fs66">namespace System.Runtime</div>
<div class="tip" id="fs67">namespace System.Runtime.CompilerServices</div>
<div class="tip" id="fs68">Multiple items<br />type ConditionalWeakTable<'TKey,'TValue (requires reference type and reference type)> =<br />  new : unit -> ConditionalWeakTable<'TKey, 'TValue><br />  member Add : key:'TKey * value:'TValue -> unit<br />  member GetOrCreateValue : key:'TKey -> 'TValue<br />  member GetValue : key:'TKey * createValueCallback:CreateValueCallback<'TKey, 'TValue> -> 'TValue<br />  member Remove : key:'TKey -> bool<br />  member TryGetValue : key:'TKey * value:'TValue -> bool<br />  nested type CreateValueCallback<br /><br />--------------------<br />System.Runtime.CompilerServices.ConditionalWeakTable() : System.Runtime.CompilerServices.ConditionalWeakTable<'TKey,'TValue></div>
<div class="tip" id="fs69">val view : ('model -> 'msg UI) (requires reference type and reference type)</div>
<div class="tip" id="fs70">val model : 'model (requires reference type)</div>
<div class="tip" id="fs71">System.Runtime.CompilerServices.ConditionalWeakTable.TryGetValue(key: 'model, value: byref<'msg UI>) : bool</div>
<div class="tip" id="fs72">val ui : 'msg UI (requires reference type)</div>
<div class="tip" id="fs73">System.Runtime.CompilerServices.ConditionalWeakTable.Add(key: 'model, value: 'msg UI) : unit</div>
<div class="tip" id="fs74">val text : text:string -> 'a UI<br /><em><br /><br /> Returns a Text display UI component.</em></div>
<div class="tip" id="fs75">val text : string</div>
<div class="tip" id="fs76">val ignore : value:'T -> unit</div>
<div class="tip" id="fs77">val input : text:string -> string UI<br /><em><br /><br /> Returns a text Input UI component.</em></div>
<div class="tip" id="fs78">val ev : (string -> unit) ref ref</div>
<div class="tip" id="fs79">val ui : string UI</div>
<div class="tip" id="fs80">val raise : (string -> unit)</div>
<div class="tip" id="fs81">val a : string</div>
<div class="tip" id="fs82">UI.Event: string -> unit</div>
<div class="tip" id="fs83">val button : text:string -> msg:'a -> 'a UI<br /><em><br /><br /> Returns a Button UI component.</em></div>
<div class="tip" id="fs84">val msg : 'a</div>
<div class="tip" id="fs85">val ev : (unit -> unit) ref ref</div>
<div class="tip" id="fs86">val ui : 'a UI</div>
<div class="tip" id="fs87">UI.Event: 'a -> unit</div>
<div class="tip" id="fs88">val div : layout:Layout -> list:'a UI list -> 'a UI<br /><em><br /><br /> Returns a section of UI components given a layout.<br /> The name div comes from HTML and represents a division (or section) of UI components.</em></div>
<div class="tip" id="fs89">val layout : Layout</div>
<div class="tip" id="fs90">Multiple items<br />val list : 'a UI list<br /><br />--------------------<br />type 'T list = List<'T></div>
<div class="tip" id="fs91">Multiple items<br />module List<br /><br />from Main<br /><br />--------------------<br />module List<br /><br />from Microsoft.FSharp.Collections<br /><br />--------------------<br />type List<'T> =<br />  | ( [] )<br />  | ( :: ) of Head: 'T * Tail: 'T list<br />    interface IReadOnlyList<'T><br />    interface IReadOnlyCollection<'T><br />    interface IEnumerable<br />    interface IEnumerable<'T><br />    member GetSlice : startIndex:int option * endIndex:int option -> 'T list<br />    member Head : 'T<br />    member IsEmpty : bool<br />    member Item : index:int -> 'T with get<br />    member Length : int<br />    member Tail : 'T list<br />    ...</div>
<div class="tip" id="fs92">val map : mapping:('T -> 'U) -> list:'T list -> 'U list</div>
<div class="tip" id="fs93">UI.UI: UI</div>
<div class="tip" id="fs94">val raise : ('a -> unit)</div>
<div class="tip" id="fs95">val a : 'a</div>
<div class="tip" id="fs96">val iter : action:('T -> unit) -> list:'T list -> unit</div>
<div class="tip" id="fs97">val i : 'a UI</div>
<div class="tip" id="fs98">val map : f:('a -> 'b) -> ui:'a UI -> 'b UI<br /><em><br /><br /> Returns a new UI component mapping the message event using the given function.</em></div>
<div class="tip" id="fs99">val f : ('a -> 'b)</div>
<div class="tip" id="fs100">val ui2 : 'b UI</div>
<div class="tip" id="fs101">val e : 'a</div>
<div class="tip" id="fs102">UI.Event: 'b -> unit</div>
<div class="tip" id="fs103">val diff : ui1:'a UI -> ui2:'b UI -> UIUpdate list<br /><em><br /><br /> Returns a list of UI updates from two UI components.</em></div>
<div class="tip" id="fs104">val ui1 : 'a UI</div>
<div class="tip" id="fs105">val update : ('c ref ref -> 'c ref ref -> unit -> unit)</div>
<div class="tip" id="fs106">val e1 : 'c ref ref</div>
<div class="tip" id="fs107">val e2 : 'c ref ref</div>
<div class="tip" id="fs108">val ev : 'c ref</div>
<div class="tip" id="fs109">val diff : (UI -> UI -> int list -> int -> UIUpdate list -> UIUpdate list)</div>
<div class="tip" id="fs110">val ui1 : UI</div>
<div class="tip" id="fs111">val ui2 : UI</div>
<div class="tip" id="fs112">val path : int list</div>
<div class="tip" id="fs113">val index : int</div>
<div class="tip" id="fs114">val diffs : UIUpdate list</div>
<div class="tip" id="fs115">module LanguagePrimitives<br /><br />from Microsoft.FSharp.Core</div>
<div class="tip" id="fs116">val PhysicalEquality : e1:'T -> e2:'T -> bool (requires reference type)</div>
<div class="tip" id="fs117">val t1 : string</div>
<div class="tip" id="fs118">val t2 : string</div>
<div class="tip" id="fs119">val e1 : unit Event</div>
<div class="tip" id="fs120">val e2 : unit Event</div>
<div class="tip" id="fs121">val e1 : string Event</div>
<div class="tip" id="fs122">val e2 : string Event</div>
<div class="tip" id="fs123">val l1 : Layout</div>
<div class="tip" id="fs124">val l2 : Layout</div>
<div class="tip" id="fs125">val l : UI list</div>
<div class="tip" id="fs126">val fold : folder:('State -> 'T -> 'State) -> state:'State -> list:'T list -> 'State</div>
<div class="tip" id="fs127">val ui : UI</div>
<div class="tip" id="fs128">val snd : tuple:('T1 * 'T2) -> 'T2</div>
<div class="tip" id="fs129">val l : Layout</div>
<div class="tip" id="fs130">val h1 : UI</div>
<div class="tip" id="fs131">val t1 : UI list</div>
<div class="tip" id="fs132">val h2 : UI</div>
<div class="tip" id="fs133">val t2 : UI list</div>
<div class="tip" id="fs134">val h3 : UI</div>
<div class="tip" id="fs135">val app : model:'a -> update:('b -> 'a -> 'a) -> view:('a -> 'b UI) -> App<'b,'a><br /><em><br /><br /> Returns a UI application from a UI model, update and view.</em></div>
<div class="tip" id="fs136">val model : 'a</div>
<div class="tip" id="fs137">val update : ('b -> 'a -> 'a)</div>
<div class="tip" id="fs138">val view : ('a -> 'b UI)</div>
<div class="tip" id="fs139">val run : nativeUI:INativeUI -> app:App<'a,'b> -> unit<br /><em><br /><br /> Runs a UI application given a native UI.</em></div>
<div class="tip" id="fs140">val nativeUI : INativeUI</div>
<div class="tip" id="fs141">val app : App<'a,'b></div>
<div class="tip" id="fs142">val handle : ('b -> 'a UI -> 'a -> unit)</div>
<div class="tip" id="fs143">val model : 'b</div>
<div class="tip" id="fs144">val newModel : 'b</div>
<div class="tip" id="fs145">App.Update: 'a -> 'b -> 'b</div>
<div class="tip" id="fs146">val newUI : 'a UI</div>
<div class="tip" id="fs147">App.View: 'b -> 'a UI</div>
<div class="tip" id="fs148">val diff : UIUpdate list</div>
<div class="tip" id="fs149">val f : (unit -> unit)</div>
<div class="tip" id="fs150">abstract member INativeUI.Send : UIUpdate list -> unit</div>
<div class="tip" id="fs151">App.Model: 'b</div>
<div class="tip" id="fs152">type Msg =<br />  | Increment<br />  | Decrement</div>
<div class="tip" id="fs153">union case Msg.Increment: Msg</div>
<div class="tip" id="fs154">union case Msg.Decrement: Msg</div>
<div class="tip" id="fs155">type Model = int</div>
<div class="tip" id="fs156">val init : i:Model -> Model</div>
<div class="tip" id="fs157">val i : Model</div>
<div class="tip" id="fs158">val update : msg:Msg -> model:int -> Model</div>
<div class="tip" id="fs159">val msg : Msg</div>
<div class="tip" id="fs160">val model : int</div>
<div class="tip" id="fs161">val view : model:Model -> Msg UI</div>
<div class="tip" id="fs162">val model : Model</div>
<div class="tip" id="fs163">Multiple items<br />module UI<br /><br />from Main<br /><br />--------------------<br />type UI =<br />  | Text of string<br />  | Input of string * string Event<br />  | Button of string * unit Event<br />  | Div of Layout * UI list<br /><em><br /><br /> Primative UI components.</em><br /><br />--------------------<br />type 'msg UI =<br />  {UI: UI;<br />   mutable Event: 'msg -> unit;}<br /><em><br /><br /> UI component including a message event.</em></div>
<div class="tip" id="fs164">val app : i:Model -> App<Msg,Model></div>
<div class="tip" id="fs165">type Msg =<br />  | Reset<br />  | Top of Msg<br />  | Bottom of Msg</div>
<div class="tip" id="fs166">union case Msg.Reset: Msg</div>
<div class="tip" id="fs167">union case Msg.Top: Counter.Msg -> Msg</div>
<div class="tip" id="fs168">module Counter<br /><br />from Main</div>
<div class="tip" id="fs169">union case Msg.Bottom: Counter.Msg -> Msg</div>
<div class="tip" id="fs170">type Model =<br />  {Top: Model;<br />   Bottom: Model;}</div>
<div class="tip" id="fs171">Model.Top: Counter.Model</div>
<div class="tip" id="fs172">Model.Bottom: Counter.Model</div>
<div class="tip" id="fs173">val init : top:Counter.Model -> bottom:Counter.Model -> Model</div>
<div class="tip" id="fs174">val top : Counter.Model</div>
<div class="tip" id="fs175">val bottom : Counter.Model</div>
<div class="tip" id="fs176">val init : i:Counter.Model -> Counter.Model</div>
<div class="tip" id="fs177">val update : msg:Msg -> model:Model -> Model</div>
<div class="tip" id="fs178">val msg : Counter.Msg</div>
<div class="tip" id="fs179">val update : msg:Counter.Msg -> model:int -> Counter.Model</div>
<div class="tip" id="fs180">val view : model:Counter.Model -> Counter.Msg UI</div>
<div class="tip" id="fs181">val app : top:Counter.Model -> bottom:Counter.Model -> App<Msg,Model></div>
<div class="tip" id="fs182">type Msg =<br />  | Insert<br />  | Remove<br />  | Modify of int * Msg</div>
<div class="tip" id="fs183">union case Msg.Insert: Msg</div>
<div class="tip" id="fs184">union case Msg.Remove: Msg</div>
<div class="tip" id="fs185">union case Msg.Modify: int * Counter.Msg -> Msg</div>
<div class="tip" id="fs186">type Model =<br />  {Counters: Model list;}</div>
<div class="tip" id="fs187">Model.Counters: Counter.Model list</div>
<div class="tip" id="fs188">val init : Model</div>
<div class="tip" id="fs189">val xs : Counter.Model list</div>
<div class="tip" id="fs190">val mapi : mapping:(int -> 'T -> 'U) -> list:'T list -> 'U list</div>
<div class="tip" id="fs191">val c : Counter.Model</div>
<div class="tip" id="fs192">val v : Counter.Msg</div>
<div class="tip" id="fs193">val app : App<Msg,Model></div>
<div class="tip" id="fs194">namespace System.Windows</div>
<div class="tip" id="fs195">namespace System.Windows.Controls</div>
<div class="tip" id="fs196">module WPF<br /><br />from Main</div>
<div class="tip" id="fs197">val CreateNaiveUI : root:ContentControl -> INativeUI</div>
<div class="tip" id="fs198">val root : ContentControl</div>
<div class="tip" id="fs199">Multiple items<br />type ContentControl =<br />  inherit Control<br />  new : unit -> ContentControl<br />  member Content : obj with get, set<br />  member ContentStringFormat : string with get, set<br />  member ContentTemplate : DataTemplate with get, set<br />  member ContentTemplateSelector : DataTemplateSelector with get, set<br />  member HasContent : bool<br />  member ShouldSerializeContent : unit -> bool<br />  static val ContentProperty : DependencyProperty<br />  static val HasContentProperty : DependencyProperty<br />  static val ContentTemplateProperty : DependencyProperty<br />  ...<br /><br />--------------------<br />ContentControl() : ContentControl</div>
<div class="tip" id="fs200">val createUI : (UI -> UIElement)</div>
<div class="tip" id="fs201">Multiple items<br />type UIElement =<br />  inherit Visual<br />  new : unit -> UIElement<br />  member AddHandler : routedEvent:RoutedEvent * handler:Delegate -> unit + 1 overload<br />  member AddToEventRoute : route:EventRoute * e:RoutedEventArgs -> unit<br />  member AllowDrop : bool with get, set<br />  member ApplyAnimationClock : dp:DependencyProperty * clock:AnimationClock -> unit + 1 overload<br />  member AreAnyTouchesCaptured : bool<br />  member AreAnyTouchesCapturedWithin : bool<br />  member AreAnyTouchesDirectlyOver : bool<br />  member AreAnyTouchesOver : bool<br />  member Arrange : finalRect:Rect -> unit<br />  ...<br /><br />--------------------<br />UIElement() : UIElement</div>
<div class="tip" id="fs202">val c : Label</div>
<div class="tip" id="fs203">Multiple items<br />type Label =<br />  inherit ContentControl<br />  new : unit -> Label<br />  member Target : UIElement with get, set<br />  static val TargetProperty : DependencyProperty<br /><br />--------------------<br />Label() : Label</div>
<div class="tip" id="fs204">Multiple items<br />union case UI.Input: string * string Event -> UI<br /><br />--------------------<br />namespace System.Windows.Input</div>
<div class="tip" id="fs205">val event : string Event</div>
<div class="tip" id="fs206">val c : TextBox</div>
<div class="tip" id="fs207">Multiple items<br />type TextBox =<br />  inherit TextBoxBase<br />  new : unit -> TextBox<br />  member CaretIndex : int with get, set<br />  member CharacterCasing : CharacterCasing with get, set<br />  member Clear : unit -> unit<br />  member GetCharacterIndexFromLineIndex : lineIndex:int -> int<br />  member GetCharacterIndexFromPoint : point:Point * snapToText:bool -> int<br />  member GetFirstVisibleLineIndex : unit -> int<br />  member GetLastVisibleLineIndex : unit -> int<br />  member GetLineIndexFromCharacterIndex : charIndex:int -> int<br />  member GetLineLength : lineIndex:int -> int<br />  ...<br /><br />--------------------<br />TextBox() : TextBox</div>
<div class="tip" id="fs208">val event : (string -> unit) ref</div>
<div class="tip" id="fs209">event Primitives.TextBoxBase.TextChanged: IEvent<TextChangedEventHandler,TextChangedEventArgs></div>
<div class="tip" id="fs210">member System.IObservable.Add : callback:('T -> unit) -> unit</div>
<div class="tip" id="fs211">val t : string</div>
<div class="tip" id="fs212">property TextBox.Text: string</div>
<div class="tip" id="fs213">val async : AsyncBuilder</div>
<div class="tip" id="fs214">Multiple items<br />type Async =<br />  static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)<br />  static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)<br />  static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool><br />  static member AwaitTask : task:Task -> Async<unit><br />  static member AwaitTask : task:Task<'T> -> Async<'T><br />  static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool><br />  static member CancelDefaultToken : unit -> unit<br />  static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>><br />  static member Choice : computations:seq<Async<'T option>> -> Async<'T option><br />  static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T><br />  ...<br /><br />--------------------<br />type Async<'T> =</div>
<div class="tip" id="fs215">static member Async.Start : computation:Async<unit> * ?cancellationToken:System.Threading.CancellationToken -> unit</div>
<div class="tip" id="fs216">Multiple items<br />type Button =<br />  inherit ButtonBase<br />  new : unit -> Button<br />  member IsCancel : bool with get, set<br />  member IsDefault : bool with get, set<br />  member IsDefaulted : bool<br />  static val IsDefaultProperty : DependencyProperty<br />  static val IsCancelProperty : DependencyProperty<br />  static val IsDefaultedProperty : DependencyProperty<br /><br />--------------------<br />Button() : Button</div>
<div class="tip" id="fs217">val event : unit Event</div>
<div class="tip" id="fs218">val c : Button</div>
<div class="tip" id="fs219">val event : (unit -> unit) ref</div>
<div class="tip" id="fs220">event Primitives.ButtonBase.Click: IEvent<RoutedEventHandler,RoutedEventArgs></div>
<div class="tip" id="fs221">Multiple items<br />val list : UI list<br /><br />--------------------<br />type 'T list = List<'T></div>
<div class="tip" id="fs222">val children : UIElement list</div>
<div class="tip" id="fs223">val c : StackPanel</div>
<div class="tip" id="fs224">Multiple items<br />type StackPanel =<br />  inherit Panel<br />  new : unit -> StackPanel<br />  member CanHorizontallyScroll : bool with get, set<br />  member CanVerticallyScroll : bool with get, set<br />  member ExtentHeight : float<br />  member ExtentWidth : float<br />  member HorizontalOffset : float<br />  member LineDown : unit -> unit<br />  member LineLeft : unit -> unit<br />  member LineRight : unit -> unit<br />  member LineUp : unit -> unit<br />  ...<br /><br />--------------------<br />StackPanel() : StackPanel</div>
<div class="tip" id="fs225">type Orientation =<br />  | Horizontal = 0<br />  | Vertical = 1</div>
<div class="tip" id="fs226">field Orientation.Vertical: Orientation = 1</div>
<div class="tip" id="fs227">field Orientation.Horizontal: Orientation = 0</div>
<div class="tip" id="fs228">property Panel.Children: UIElementCollection</div>
<div class="tip" id="fs229">UIElementCollection.Add(element: UIElement) : int</div>
<div class="tip" id="fs230">val locatePanel : (int list -> Panel)</div>
<div class="tip" id="fs231">val loc : int list</div>
<div class="tip" id="fs232">type Panel =<br />  inherit FrameworkElement<br />  member Background : Brush with get, set<br />  member Children : UIElementCollection<br />  member HasLogicalOrientationPublic : bool<br />  member IsItemsHost : bool with get, set<br />  member LogicalOrientationPublic : Orientation<br />  member ShouldSerializeChildren : unit -> bool<br />  static val BackgroundProperty : DependencyProperty<br />  static val IsItemsHostProperty : DependencyProperty<br />  static val ZIndexProperty : DependencyProperty<br />  static member GetZIndex : element:UIElement -> int<br />  ...</div>
<div class="tip" id="fs233">property ContentControl.Content: obj</div>
<div class="tip" id="fs234">val xs : int list</div>
<div class="tip" id="fs235">val uiUpdate : (UIUpdate -> unit)</div>
<div class="tip" id="fs236">val u : UIUpdate</div>
<div class="tip" id="fs237">val element : UIElement</div>
<div class="tip" id="fs238">val c : UIElementCollection</div>
<div class="tip" id="fs239">UIElementCollection.RemoveAt(index: int) : unit</div>
<div class="tip" id="fs240">UIElementCollection.Insert(index: int, element: UIElement) : unit</div>
<div class="tip" id="fs241">Multiple items<br />val list : UIUpdate list<br /><br />--------------------<br />type 'T list = List<'T></div>
<div class="tip" id="fs242">property Threading.DispatcherObject.Dispatcher: Threading.Dispatcher</div>
<div class="tip" id="fs243">Threading.Dispatcher.Invoke<'TResult>(callback: System.Func<'TResult>) : 'TResult<br />   <em>(+0 other overloads)</em><br />Threading.Dispatcher.Invoke(callback: System.Action) : unit<br />   <em>(+0 other overloads)</em><br />Threading.Dispatcher.Invoke(method: System.Delegate, [<System.ParamArray>] args: obj []) : obj<br />   <em>(+0 other overloads)</em><br />Threading.Dispatcher.Invoke(priority: Threading.DispatcherPriority, method: System.Delegate) : obj<br />   <em>(+0 other overloads)</em><br />Threading.Dispatcher.Invoke<'TResult>(callback: System.Func<'TResult>, priority: Threading.DispatcherPriority) : 'TResult<br />   <em>(+0 other overloads)</em><br />Threading.Dispatcher.Invoke(callback: System.Action, priority: Threading.DispatcherPriority) : unit<br />   <em>(+0 other overloads)</em><br />Threading.Dispatcher.Invoke(method: System.Delegate, timeout: System.TimeSpan, [<System.ParamArray>] args: obj []) : obj<br />   <em>(+0 other overloads)</em><br />Threading.Dispatcher.Invoke(method: System.Delegate, priority: Threading.DispatcherPriority, [<System.ParamArray>] args: obj []) : obj<br />   <em>(+0 other overloads)</em><br />Threading.Dispatcher.Invoke(priority: Threading.DispatcherPriority, timeout: System.TimeSpan, method: System.Delegate) : obj<br />   <em>(+0 other overloads)</em><br />Threading.Dispatcher.Invoke(priority: Threading.DispatcherPriority, method: System.Delegate, arg: obj) : obj<br />   <em>(+0 other overloads)</em></div>
F# Implementation of The Elm Architecture
2016-06-20T00:00:00+01:00
http://anthonylloyd.github.io/blog/2016/06/20/fsharp-elm-part1
<p>This is a prototype implementation of <a href="http://guide.elm-lang.org/architecture/index.html">The Elm Architecture</a> in F#.
This post covers the UI implementation and a follow up <a href="/blog/2016/07/01/fsharp-elm-part2">post</a> covers using it with WPF and Xamarin.</p>
<h2><a name="Background" class="anchor" href="#Background">Background</a></h2>
<p><a href="http://guide.elm-lang.org/architecture/index.html">The Elm Architecture</a> is a simple pattern for creating functional UIs.
Due to its modularity and composability it makes UI code easier to write, understand, test and reuse.</p>
<p>The UI implementation is a complete representation of the native UI.
A minimal list of UI updates is calculated from the current and future UI.
This is then used to update the native UI with as little interaction as possible.
This means the native UI renders faster and is more responsive.
It also means multiple native UI platforms can be targeted with the same application code.</p>
<h2><a name="UI-types" class="anchor" href="#UI-types">UI types</a></h2>
<p>UI events are implemented using simple functions that are mapped and combined up to the top level message event.
At the primitive UI component level events are implemented using a double <code>ref</code>.
This is so the native UI events only have to be hooked up once.
The events can be redirected quickly as the UI changes without the potential memory leak that arises from a single <code>ref</code> implementation.</p>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="c">/// Message event used on the primitive UI components.</span>
<span class="k">type</span> <span class="ta">'</span><span class="id">msg</span> <span onmouseout="hideTip(event, 'fs25', 56)" onmouseover="showTip(event, 'fs25', 56)" class="rt">Event</span> <span class="o">=</span> <span class="pn">(</span><span class="ta">'</span><span class="id">msg</span><span class="k">-></span><span onmouseout="hideTip(event, 'fs26', 57)" onmouseover="showTip(event, 'fs26', 57)" class="rt">unit</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs27', 58)" onmouseover="showTip(event, 'fs27', 58)" class="rt">ref</span> <span onmouseout="hideTip(event, 'fs27', 59)" onmouseover="showTip(event, 'fs27', 59)" class="rt">ref</span>
<span class="c">/// Layout for a section of UI components.</span>
<span class="k">type</span> <span onmouseout="hideTip(event, 'fs28', 60)" onmouseover="showTip(event, 'fs28', 60)" class="rt">Layout</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs29', 61)" onmouseover="showTip(event, 'fs29', 61)" class="uc">Horizontal</span> <span class="pn">|</span> <span onmouseout="hideTip(event, 'fs30', 62)" onmouseover="showTip(event, 'fs30', 62)" class="uc">Vertical</span>
<span class="c">/// Primitive UI components.</span>
<span class="k">type</span> <span onmouseout="hideTip(event, 'fs31', 63)" onmouseover="showTip(event, 'fs31', 63)" class="rt">UI</span> <span class="o">=</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs32', 64)" onmouseover="showTip(event, 'fs32', 64)" class="uc">Text</span> <span class="k">of</span> <span onmouseout="hideTip(event, 'fs33', 65)" onmouseover="showTip(event, 'fs33', 65)" class="rt">string</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs34', 66)" onmouseover="showTip(event, 'fs34', 66)" class="uc">Input</span> <span class="k">of</span> <span onmouseout="hideTip(event, 'fs33', 67)" onmouseover="showTip(event, 'fs33', 67)" class="rt">string</span> <span class="pn">*</span> <span onmouseout="hideTip(event, 'fs33', 68)" onmouseover="showTip(event, 'fs33', 68)" class="rt">string</span> <span onmouseout="hideTip(event, 'fs25', 69)" onmouseover="showTip(event, 'fs25', 69)" class="rt">Event</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs35', 70)" onmouseover="showTip(event, 'fs35', 70)" class="uc">Button</span> <span class="k">of</span> <span onmouseout="hideTip(event, 'fs33', 71)" onmouseover="showTip(event, 'fs33', 71)" class="rt">string</span> <span class="pn">*</span> <span onmouseout="hideTip(event, 'fs26', 72)" onmouseover="showTip(event, 'fs26', 72)" class="rt">unit</span> <span onmouseout="hideTip(event, 'fs25', 73)" onmouseover="showTip(event, 'fs25', 73)" class="rt">Event</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs36', 74)" onmouseover="showTip(event, 'fs36', 74)" class="uc">Div</span> <span class="k">of</span> <span onmouseout="hideTip(event, 'fs28', 75)" onmouseover="showTip(event, 'fs28', 75)" class="rt">Layout</span> <span class="pn">*</span> <span onmouseout="hideTip(event, 'fs31', 76)" onmouseover="showTip(event, 'fs31', 76)" class="rt">UI</span> <span onmouseout="hideTip(event, 'fs37', 77)" onmouseover="showTip(event, 'fs37', 77)" class="rt">list</span>
<span class="c">/// UI component update and event redirection.</span>
<span class="k">type</span> <span onmouseout="hideTip(event, 'fs38', 78)" onmouseover="showTip(event, 'fs38', 78)" class="rt">UIUpdate</span> <span class="o">=</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs39', 79)" onmouseover="showTip(event, 'fs39', 79)" class="uc">InsertUI</span> <span class="k">of</span> <span onmouseout="hideTip(event, 'fs40', 80)" onmouseover="showTip(event, 'fs40', 80)" class="vt">int</span> <span onmouseout="hideTip(event, 'fs37', 81)" onmouseover="showTip(event, 'fs37', 81)" class="rt">list</span> <span class="pn">*</span> <span onmouseout="hideTip(event, 'fs31', 82)" onmouseover="showTip(event, 'fs31', 82)" class="rt">UI</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs41', 83)" onmouseover="showTip(event, 'fs41', 83)" class="uc">UpdateUI</span> <span class="k">of</span> <span onmouseout="hideTip(event, 'fs40', 84)" onmouseover="showTip(event, 'fs40', 84)" class="vt">int</span> <span onmouseout="hideTip(event, 'fs37', 85)" onmouseover="showTip(event, 'fs37', 85)" class="rt">list</span> <span class="pn">*</span> <span onmouseout="hideTip(event, 'fs31', 86)" onmouseover="showTip(event, 'fs31', 86)" class="rt">UI</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs42', 87)" onmouseover="showTip(event, 'fs42', 87)" class="uc">ReplaceUI</span> <span class="k">of</span> <span onmouseout="hideTip(event, 'fs40', 88)" onmouseover="showTip(event, 'fs40', 88)" class="vt">int</span> <span onmouseout="hideTip(event, 'fs37', 89)" onmouseover="showTip(event, 'fs37', 89)" class="rt">list</span> <span class="pn">*</span> <span onmouseout="hideTip(event, 'fs31', 90)" onmouseover="showTip(event, 'fs31', 90)" class="rt">UI</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs43', 91)" onmouseover="showTip(event, 'fs43', 91)" class="uc">RemoveUI</span> <span class="k">of</span> <span onmouseout="hideTip(event, 'fs40', 92)" onmouseover="showTip(event, 'fs40', 92)" class="vt">int</span> <span onmouseout="hideTip(event, 'fs37', 93)" onmouseover="showTip(event, 'fs37', 93)" class="rt">list</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs44', 94)" onmouseover="showTip(event, 'fs44', 94)" class="uc">EventUI</span> <span class="k">of</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs26', 95)" onmouseover="showTip(event, 'fs26', 95)" class="rt">unit</span><span class="k">-></span><span onmouseout="hideTip(event, 'fs26', 96)" onmouseover="showTip(event, 'fs26', 96)" class="rt">unit</span><span class="pn">)</span>
<span class="c">/// UI component including a message event.</span>
<span class="k">type</span> <span class="ta">'</span><span class="id">msg</span> <span onmouseout="hideTip(event, 'fs45', 97)" onmouseover="showTip(event, 'fs45', 97)" class="rt">UI</span> <span class="o">=</span> <span class="pn">{</span><span onmouseout="hideTip(event, 'fs46', 98)" onmouseover="showTip(event, 'fs46', 98)" class="id">UI</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs45', 99)" onmouseover="showTip(event, 'fs45', 99)" class="rt">UI</span><span class="pn">;</span><span class="k">mutable</span> <span onmouseout="hideTip(event, 'fs47', 100)" onmouseover="showTip(event, 'fs47', 100)" class="mv">Event</span><span class="pn">:</span><span class="ta">'</span><span class="id">msg</span><span class="k">-></span><span onmouseout="hideTip(event, 'fs26', 101)" onmouseover="showTip(event, 'fs26', 101)" class="rt">unit</span><span class="pn">}</span>
<span class="c">/// UI application.</span>
<span class="k">type</span> <span onmouseout="hideTip(event, 'fs48', 102)" onmouseover="showTip(event, 'fs48', 102)" class="rt">App</span><span class="pn"><</span><span class="ta">'</span><span class="id">msg</span><span class="pn">,</span><span class="ta">'</span><span class="id">model</span><span class="pn">></span> <span class="o">=</span>
<span class="pn">{</span>
<span onmouseout="hideTip(event, 'fs49', 103)" onmouseover="showTip(event, 'fs49', 103)" class="id">Model</span><span class="pn">:</span><span class="ta">'</span><span class="id">model</span>
<span onmouseout="hideTip(event, 'fs50', 104)" onmouseover="showTip(event, 'fs50', 104)" class="fn">Update</span><span class="pn">:</span><span class="ta">'</span><span class="id">msg</span><span class="k">-></span><span class="ta">'</span><span class="id">model</span><span class="k">-></span><span class="ta">'</span><span class="id">model</span>
<span onmouseout="hideTip(event, 'fs51', 105)" onmouseover="showTip(event, 'fs51', 105)" class="fn">View</span><span class="pn">:</span><span class="ta">'</span><span class="id">model</span><span class="k">-></span><span class="ta">'</span><span class="id">msg</span> <span onmouseout="hideTip(event, 'fs45', 106)" onmouseover="showTip(event, 'fs45', 106)" class="rt">UI</span>
<span class="pn">}</span>
<span class="c">/// Native UI interface.</span>
<span class="k">type</span> <span onmouseout="hideTip(event, 'fs52', 107)" onmouseover="showTip(event, 'fs52', 107)" class="if">INativeUI</span> <span class="o">=</span>
<span class="k">abstract</span> <span class="k">member</span> <span class="fn">Send</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs38', 108)" onmouseover="showTip(event, 'fs38', 108)" class="rt">UIUpdate</span> <span onmouseout="hideTip(event, 'fs37', 109)" onmouseover="showTip(event, 'fs37', 109)" class="rt">list</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs26', 110)" onmouseover="showTip(event, 'fs26', 110)" class="rt">unit</span>
</code></pre>
<h2><a name="UI-module" class="anchor" href="#UI-module">UI module</a></h2>
<p>The UI rendering can be made even faster by using the memoize function in larger views.
This stores a weak reference to a model and its view output.
It makes the view and diff functions quicker and can remove unnecessary UI updates.</p>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="pn">[<</span><span onmouseout="hideTip(event, 'fs53', 111)" onmouseover="showTip(event, 'fs53', 111)" class="rt">CompilationRepresentation</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs54', 112)" onmouseover="showTip(event, 'fs54', 112)" class="vt">CompilationRepresentationFlags</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs55', 113)" onmouseover="showTip(event, 'fs55', 113)" class="id">ModuleSuffix</span><span class="pn">)</span><span class="pn">>]</span>
<span class="k">module</span> <span onmouseout="hideTip(event, 'fs45', 114)" onmouseover="showTip(event, 'fs45', 114)" class="m">UI</span> <span class="o">=</span>
<span class="c">/// Memoize view generation from model object references.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs56', 115)" onmouseover="showTip(event, 'fs56', 115)" class="fn">memoize</span><span class="pn"><</span><span class="ta">'</span><span class="id">model</span> <span class="pn">,</span><span class="ta">'</span><span class="id">msg</span> <span class="k">when</span> <span class="ta">'</span><span class="id">model</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs57', 116)" onmouseover="showTip(event, 'fs57', 116)" class="id">not</span> <span class="k">struct</span> <span class="k">and</span> <span class="ta">'</span><span class="id">msg</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs57', 117)" onmouseover="showTip(event, 'fs57', 117)" class="id">not</span> <span class="k">struct</span><span class="pn">></span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs58', 118)" onmouseover="showTip(event, 'fs58', 118)" class="id">d</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs59', 119)" onmouseover="showTip(event, 'fs59', 119)" class="rt">ConditionalWeakTable</span><span class="pn"><</span><span class="ta">'</span><span class="id">model</span><span class="pn">,</span><span class="ta">'</span><span class="id">msg</span> <span onmouseout="hideTip(event, 'fs45', 120)" onmouseover="showTip(event, 'fs45', 120)" class="rt">UI</span><span class="pn">></span><span class="pn">(</span><span class="pn">)</span>
<span class="k">fun</span> <span onmouseout="hideTip(event, 'fs60', 121)" onmouseover="showTip(event, 'fs60', 121)" class="fn">view</span> <span onmouseout="hideTip(event, 'fs61', 122)" onmouseover="showTip(event, 'fs61', 122)" class="id">model</span> <span class="k">-></span>
<span class="k">match</span> <span onmouseout="hideTip(event, 'fs58', 123)" onmouseover="showTip(event, 'fs58', 123)" class="fn">d</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs62', 124)" onmouseover="showTip(event, 'fs62', 124)" class="id">TryGetValue</span> <span onmouseout="hideTip(event, 'fs61', 125)" onmouseover="showTip(event, 'fs61', 125)" class="id">model</span> <span class="k">with</span>
<span class="pn">|</span><span class="k">true</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs63', 126)" onmouseover="showTip(event, 'fs63', 126)" class="id">ui</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs63', 127)" onmouseover="showTip(event, 'fs63', 127)" class="id">ui</span>
<span class="pn">|</span><span class="k">false</span><span class="pn">,</span><span class="id">_</span> <span class="k">-></span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs63', 128)" onmouseover="showTip(event, 'fs63', 128)" class="id">ui</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs60', 129)" onmouseover="showTip(event, 'fs60', 129)" class="fn">view</span> <span onmouseout="hideTip(event, 'fs61', 130)" onmouseover="showTip(event, 'fs61', 130)" class="id">model</span>
<span onmouseout="hideTip(event, 'fs58', 131)" onmouseover="showTip(event, 'fs58', 131)" class="fn">d</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs64', 132)" onmouseover="showTip(event, 'fs64', 132)" class="id">Add</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs61', 133)" onmouseover="showTip(event, 'fs61', 133)" class="id">model</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs63', 134)" onmouseover="showTip(event, 'fs63', 134)" class="id">ui</span><span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs63', 135)" onmouseover="showTip(event, 'fs63', 135)" class="id">ui</span>
<span class="c">/// Returns a Text display UI component.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs65', 136)" onmouseover="showTip(event, 'fs65', 136)" class="fn">text</span> <span onmouseout="hideTip(event, 'fs66', 137)" onmouseover="showTip(event, 'fs66', 137)" class="id">text</span> <span class="o">=</span> <span class="pn">{</span><span onmouseout="hideTip(event, 'fs45', 138)" onmouseover="showTip(event, 'fs45', 138)" class="id">UI</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs32', 139)" onmouseover="showTip(event, 'fs32', 139)" class="uc">Text</span> <span onmouseout="hideTip(event, 'fs66', 140)" onmouseover="showTip(event, 'fs66', 140)" class="id">text</span><span class="pn">;</span><span onmouseout="hideTip(event, 'fs25', 141)" onmouseover="showTip(event, 'fs25', 141)" class="mv">Event</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs67', 142)" onmouseover="showTip(event, 'fs67', 142)" class="fn">ignore</span><span class="pn">}</span>
<span class="c">/// Returns a text Input UI component.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs68', 143)" onmouseover="showTip(event, 'fs68', 143)" class="fn">input</span> <span onmouseout="hideTip(event, 'fs66', 144)" onmouseover="showTip(event, 'fs66', 144)" class="id">text</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs69', 145)" onmouseover="showTip(event, 'fs69', 145)" class="mv">ev</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs27', 146)" onmouseover="showTip(event, 'fs27', 146)" class="fn">ref</span> <span onmouseout="hideTip(event, 'fs67', 147)" onmouseover="showTip(event, 'fs67', 147)" class="fn">ignore</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs27', 148)" onmouseover="showTip(event, 'fs27', 148)" class="fn">ref</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs70', 149)" onmouseover="showTip(event, 'fs70', 149)" class="id">ui</span> <span class="o">=</span> <span class="pn">{</span><span onmouseout="hideTip(event, 'fs45', 150)" onmouseover="showTip(event, 'fs45', 150)" class="id">UI</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs34', 151)" onmouseover="showTip(event, 'fs34', 151)" class="uc">Input</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs66', 152)" onmouseover="showTip(event, 'fs66', 152)" class="id">text</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs69', 153)" onmouseover="showTip(event, 'fs69', 153)" class="mv">ev</span><span class="pn">)</span><span class="pn">;</span><span onmouseout="hideTip(event, 'fs25', 154)" onmouseover="showTip(event, 'fs25', 154)" class="mv">Event</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs67', 155)" onmouseover="showTip(event, 'fs67', 155)" class="fn">ignore</span><span class="pn">}</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs71', 156)" onmouseover="showTip(event, 'fs71', 156)" class="fn">raise</span> <span onmouseout="hideTip(event, 'fs72', 157)" onmouseover="showTip(event, 'fs72', 157)" class="id">a</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs70', 158)" onmouseover="showTip(event, 'fs70', 158)" class="mv">ui</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs73', 159)" onmouseover="showTip(event, 'fs73', 159)" class="id">Event</span> <span onmouseout="hideTip(event, 'fs72', 160)" onmouseover="showTip(event, 'fs72', 160)" class="id">a</span>
<span class="pn">(</span><span class="o">!</span><span onmouseout="hideTip(event, 'fs69', 161)" onmouseover="showTip(event, 'fs69', 161)" class="mv">ev</span><span class="pn">)</span><span class="o">:=</span><span onmouseout="hideTip(event, 'fs71', 162)" onmouseover="showTip(event, 'fs71', 162)" class="fn">raise</span>
<span onmouseout="hideTip(event, 'fs70', 163)" onmouseover="showTip(event, 'fs70', 163)" class="id">ui</span>
<span class="c">/// Returns a Button UI component.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs74', 164)" onmouseover="showTip(event, 'fs74', 164)" class="fn">button</span> <span onmouseout="hideTip(event, 'fs66', 165)" onmouseover="showTip(event, 'fs66', 165)" class="id">text</span> <span onmouseout="hideTip(event, 'fs75', 166)" onmouseover="showTip(event, 'fs75', 166)" class="id">msg</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs76', 167)" onmouseover="showTip(event, 'fs76', 167)" class="mv">ev</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs27', 168)" onmouseover="showTip(event, 'fs27', 168)" class="fn">ref</span> <span onmouseout="hideTip(event, 'fs67', 169)" onmouseover="showTip(event, 'fs67', 169)" class="fn">ignore</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs27', 170)" onmouseover="showTip(event, 'fs27', 170)" class="fn">ref</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs77', 171)" onmouseover="showTip(event, 'fs77', 171)" class="id">ui</span> <span class="o">=</span> <span class="pn">{</span><span onmouseout="hideTip(event, 'fs45', 172)" onmouseover="showTip(event, 'fs45', 172)" class="id">UI</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs35', 173)" onmouseover="showTip(event, 'fs35', 173)" class="uc">Button</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs66', 174)" onmouseover="showTip(event, 'fs66', 174)" class="id">text</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs76', 175)" onmouseover="showTip(event, 'fs76', 175)" class="mv">ev</span><span class="pn">)</span><span class="pn">;</span><span onmouseout="hideTip(event, 'fs25', 176)" onmouseover="showTip(event, 'fs25', 176)" class="mv">Event</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs67', 177)" onmouseover="showTip(event, 'fs67', 177)" class="fn">ignore</span><span class="pn">}</span>
<span class="pn">(</span><span class="o">!</span><span onmouseout="hideTip(event, 'fs76', 178)" onmouseover="showTip(event, 'fs76', 178)" class="mv">ev</span><span class="pn">)</span><span class="o">:=</span><span class="k">fun</span> <span class="pn">(</span><span class="pn">)</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs77', 179)" onmouseover="showTip(event, 'fs77', 179)" class="mv">ui</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs78', 180)" onmouseover="showTip(event, 'fs78', 180)" class="id">Event</span> <span onmouseout="hideTip(event, 'fs75', 181)" onmouseover="showTip(event, 'fs75', 181)" class="id">msg</span>
<span onmouseout="hideTip(event, 'fs77', 182)" onmouseover="showTip(event, 'fs77', 182)" class="id">ui</span>
<span class="c">/// Returns a section of UI components given a layout.</span>
<span class="c">/// The name div comes from HTML and represents a division (or section) of the UI.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs79', 183)" onmouseover="showTip(event, 'fs79', 183)" class="fn">div</span> <span onmouseout="hideTip(event, 'fs80', 184)" onmouseover="showTip(event, 'fs80', 184)" class="id">layout</span> <span onmouseout="hideTip(event, 'fs81', 185)" onmouseover="showTip(event, 'fs81', 185)" class="id">list</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs77', 186)" onmouseover="showTip(event, 'fs77', 186)" class="id">ui</span> <span class="o">=</span> <span class="pn">{</span><span onmouseout="hideTip(event, 'fs45', 187)" onmouseover="showTip(event, 'fs45', 187)" class="id">UI</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs36', 188)" onmouseover="showTip(event, 'fs36', 188)" class="uc">Div</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs80', 189)" onmouseover="showTip(event, 'fs80', 189)" class="id">layout</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs82', 190)" onmouseover="showTip(event, 'fs82', 190)" class="m">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs83', 191)" onmouseover="showTip(event, 'fs83', 191)" class="id">map</span> <span class="pn">(</span><span class="k">fun</span> <span onmouseout="hideTip(event, 'fs77', 192)" onmouseover="showTip(event, 'fs77', 192)" class="id">ui</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs77', 193)" onmouseover="showTip(event, 'fs77', 193)" class="id">ui</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs84', 194)" onmouseover="showTip(event, 'fs84', 194)" class="id">UI</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs81', 195)" onmouseover="showTip(event, 'fs81', 195)" class="id">list</span><span class="pn">)</span><span class="pn">;</span><span onmouseout="hideTip(event, 'fs25', 196)" onmouseover="showTip(event, 'fs25', 196)" class="mv">Event</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs67', 197)" onmouseover="showTip(event, 'fs67', 197)" class="fn">ignore</span><span class="pn">}</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs85', 198)" onmouseover="showTip(event, 'fs85', 198)" class="fn">raise</span> <span onmouseout="hideTip(event, 'fs86', 199)" onmouseover="showTip(event, 'fs86', 199)" class="id">a</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs77', 200)" onmouseover="showTip(event, 'fs77', 200)" class="mv">ui</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs78', 201)" onmouseover="showTip(event, 'fs78', 201)" class="id">Event</span> <span onmouseout="hideTip(event, 'fs86', 202)" onmouseover="showTip(event, 'fs86', 202)" class="id">a</span>
<span onmouseout="hideTip(event, 'fs82', 203)" onmouseover="showTip(event, 'fs82', 203)" class="m">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs87', 204)" onmouseover="showTip(event, 'fs87', 204)" class="id">iter</span> <span class="pn">(</span><span class="k">fun</span> <span onmouseout="hideTip(event, 'fs88', 205)" onmouseover="showTip(event, 'fs88', 205)" class="id">i</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs88', 206)" onmouseover="showTip(event, 'fs88', 206)" class="mv">i</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs78', 207)" onmouseover="showTip(event, 'fs78', 207)" class="id">Event</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs85', 208)" onmouseover="showTip(event, 'fs85', 208)" class="fn">raise</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs81', 209)" onmouseover="showTip(event, 'fs81', 209)" class="id">list</span>
<span onmouseout="hideTip(event, 'fs77', 210)" onmouseover="showTip(event, 'fs77', 210)" class="id">ui</span>
<span class="c">/// Returns a new UI component mapping the message event using the given function.</span>
<span class="k">let</span> <span class="k">rec</span> <span onmouseout="hideTip(event, 'fs89', 211)" onmouseover="showTip(event, 'fs89', 211)" class="fn">map</span> <span onmouseout="hideTip(event, 'fs90', 212)" onmouseover="showTip(event, 'fs90', 212)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs77', 213)" onmouseover="showTip(event, 'fs77', 213)" class="id">ui</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs91', 214)" onmouseover="showTip(event, 'fs91', 214)" class="id">ui2</span> <span class="o">=</span> <span class="pn">{</span><span onmouseout="hideTip(event, 'fs45', 215)" onmouseover="showTip(event, 'fs45', 215)" class="id">UI</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs77', 216)" onmouseover="showTip(event, 'fs77', 216)" class="id">ui</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs84', 217)" onmouseover="showTip(event, 'fs84', 217)" class="id">UI</span><span class="pn">;</span><span onmouseout="hideTip(event, 'fs25', 218)" onmouseover="showTip(event, 'fs25', 218)" class="mv">Event</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs67', 219)" onmouseover="showTip(event, 'fs67', 219)" class="fn">ignore</span><span class="pn">}</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs85', 220)" onmouseover="showTip(event, 'fs85', 220)" class="fn">raise</span> <span onmouseout="hideTip(event, 'fs92', 221)" onmouseover="showTip(event, 'fs92', 221)" class="id">e</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs90', 222)" onmouseover="showTip(event, 'fs90', 222)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs92', 223)" onmouseover="showTip(event, 'fs92', 223)" class="id">e</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs91', 224)" onmouseover="showTip(event, 'fs91', 224)" class="mv">ui2</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs93', 225)" onmouseover="showTip(event, 'fs93', 225)" class="id">Event</span>
<span onmouseout="hideTip(event, 'fs77', 226)" onmouseover="showTip(event, 'fs77', 226)" class="mv">ui</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs78', 227)" onmouseover="showTip(event, 'fs78', 227)" class="id">Event</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs85', 228)" onmouseover="showTip(event, 'fs85', 228)" class="fn">raise</span>
<span onmouseout="hideTip(event, 'fs91', 229)" onmouseover="showTip(event, 'fs91', 229)" class="id">ui2</span>
<span class="c">/// Returns a list of UI updates from two UI components.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs94', 230)" onmouseover="showTip(event, 'fs94', 230)" class="fn">diff</span> <span onmouseout="hideTip(event, 'fs95', 231)" onmouseover="showTip(event, 'fs95', 231)" class="id">ui1</span> <span onmouseout="hideTip(event, 'fs91', 232)" onmouseover="showTip(event, 'fs91', 232)" class="id">ui2</span> <span class="o">=</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs96', 233)" onmouseover="showTip(event, 'fs96', 233)" class="fn">update</span> <span onmouseout="hideTip(event, 'fs97', 234)" onmouseover="showTip(event, 'fs97', 234)" class="mv">e1</span> <span onmouseout="hideTip(event, 'fs98', 235)" onmouseover="showTip(event, 'fs98', 235)" class="mv">e2</span> <span class="o">=</span> <span class="k">fun</span> <span class="pn">(</span><span class="pn">)</span> <span class="k">-></span> <span class="k">let</span> <span onmouseout="hideTip(event, 'fs99', 236)" onmouseover="showTip(event, 'fs99', 236)" class="mv">ev</span> <span class="o">=</span> <span class="o">!</span><span onmouseout="hideTip(event, 'fs97', 237)" onmouseover="showTip(event, 'fs97', 237)" class="mv">e1</span> <span class="k">in</span> <span onmouseout="hideTip(event, 'fs99', 238)" onmouseover="showTip(event, 'fs99', 238)" class="mv">ev</span><span class="o">:=</span><span class="o">!</span><span class="pn">(</span><span class="o">!</span><span onmouseout="hideTip(event, 'fs98', 239)" onmouseover="showTip(event, 'fs98', 239)" class="mv">e2</span><span class="pn">)</span><span class="pn">;</span> <span onmouseout="hideTip(event, 'fs98', 240)" onmouseover="showTip(event, 'fs98', 240)" class="mv">e2</span><span class="o">:=</span><span onmouseout="hideTip(event, 'fs99', 241)" onmouseover="showTip(event, 'fs99', 241)" class="mv">ev</span>
<span class="k">let</span> <span class="k">rec</span> <span onmouseout="hideTip(event, 'fs100', 242)" onmouseover="showTip(event, 'fs100', 242)" class="fn">diff</span> <span onmouseout="hideTip(event, 'fs101', 243)" onmouseover="showTip(event, 'fs101', 243)" class="id">ui1</span> <span onmouseout="hideTip(event, 'fs102', 244)" onmouseover="showTip(event, 'fs102', 244)" class="id">ui2</span> <span onmouseout="hideTip(event, 'fs103', 245)" onmouseover="showTip(event, 'fs103', 245)" class="id">path</span> <span onmouseout="hideTip(event, 'fs104', 246)" onmouseover="showTip(event, 'fs104', 246)" class="id">index</span> <span onmouseout="hideTip(event, 'fs105', 247)" onmouseover="showTip(event, 'fs105', 247)" class="id">diffs</span> <span class="o">=</span>
<span class="k">match</span> <span onmouseout="hideTip(event, 'fs101', 248)" onmouseover="showTip(event, 'fs101', 248)" class="id">ui1</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs102', 249)" onmouseover="showTip(event, 'fs102', 249)" class="id">ui2</span> <span class="k">with</span>
<span class="pn">|</span><span class="id">_</span> <span class="k">when</span> <span onmouseout="hideTip(event, 'fs106', 250)" onmouseover="showTip(event, 'fs106', 250)" class="m">LanguagePrimitives</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs107', 251)" onmouseover="showTip(event, 'fs107', 251)" class="id">PhysicalEquality</span> <span onmouseout="hideTip(event, 'fs101', 252)" onmouseover="showTip(event, 'fs101', 252)" class="id">ui1</span> <span onmouseout="hideTip(event, 'fs102', 253)" onmouseover="showTip(event, 'fs102', 253)" class="id">ui2</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs105', 254)" onmouseover="showTip(event, 'fs105', 254)" class="id">diffs</span>
<span class="pn">|</span><span onmouseout="hideTip(event, 'fs32', 255)" onmouseover="showTip(event, 'fs32', 255)" class="uc">Text</span> <span onmouseout="hideTip(event, 'fs108', 256)" onmouseover="showTip(event, 'fs108', 256)" class="id">t1</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs32', 257)" onmouseover="showTip(event, 'fs32', 257)" class="uc">Text</span> <span onmouseout="hideTip(event, 'fs109', 258)" onmouseover="showTip(event, 'fs109', 258)" class="id">t2</span> <span class="k">-></span> <span class="k">if</span> <span class="id">t1</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs109', 259)" onmouseover="showTip(event, 'fs109', 259)" class="id">t2</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs105', 260)" onmouseover="showTip(event, 'fs105', 260)" class="id">diffs</span> <span class="k">else</span> <span onmouseout="hideTip(event, 'fs41', 261)" onmouseover="showTip(event, 'fs41', 261)" class="uc">UpdateUI</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs103', 262)" onmouseover="showTip(event, 'fs103', 262)" class="id">path</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs102', 263)" onmouseover="showTip(event, 'fs102', 263)" class="id">ui2</span><span class="pn">)</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs105', 264)" onmouseover="showTip(event, 'fs105', 264)" class="id">diffs</span>
<span class="pn">|</span><span onmouseout="hideTip(event, 'fs35', 265)" onmouseover="showTip(event, 'fs35', 265)" class="uc">Button</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs108', 266)" onmouseover="showTip(event, 'fs108', 266)" class="id">t1</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs110', 267)" onmouseover="showTip(event, 'fs110', 267)" class="mv">e1</span><span class="pn">)</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs35', 268)" onmouseover="showTip(event, 'fs35', 268)" class="uc">Button</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs109', 269)" onmouseover="showTip(event, 'fs109', 269)" class="id">t2</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs111', 270)" onmouseover="showTip(event, 'fs111', 270)" class="mv">e2</span><span class="pn">)</span> <span class="k">-></span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs108', 271)" onmouseover="showTip(event, 'fs108', 271)" class="id">t1</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs109', 272)" onmouseover="showTip(event, 'fs109', 272)" class="id">t2</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs44', 273)" onmouseover="showTip(event, 'fs44', 273)" class="uc">EventUI</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs96', 274)" onmouseover="showTip(event, 'fs96', 274)" class="fn">update</span> <span onmouseout="hideTip(event, 'fs110', 275)" onmouseover="showTip(event, 'fs110', 275)" class="mv">e1</span> <span onmouseout="hideTip(event, 'fs111', 276)" onmouseover="showTip(event, 'fs111', 276)" class="mv">e2</span><span class="pn">)</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs105', 277)" onmouseover="showTip(event, 'fs105', 277)" class="id">diffs</span>
<span class="k">else</span> <span onmouseout="hideTip(event, 'fs44', 278)" onmouseover="showTip(event, 'fs44', 278)" class="uc">EventUI</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs96', 279)" onmouseover="showTip(event, 'fs96', 279)" class="fn">update</span> <span onmouseout="hideTip(event, 'fs110', 280)" onmouseover="showTip(event, 'fs110', 280)" class="mv">e1</span> <span onmouseout="hideTip(event, 'fs111', 281)" onmouseover="showTip(event, 'fs111', 281)" class="mv">e2</span><span class="pn">)</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs41', 282)" onmouseover="showTip(event, 'fs41', 282)" class="uc">UpdateUI</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs103', 283)" onmouseover="showTip(event, 'fs103', 283)" class="id">path</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs102', 284)" onmouseover="showTip(event, 'fs102', 284)" class="id">ui2</span><span class="pn">)</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs105', 285)" onmouseover="showTip(event, 'fs105', 285)" class="id">diffs</span>
<span class="pn">|</span><span onmouseout="hideTip(event, 'fs34', 286)" onmouseover="showTip(event, 'fs34', 286)" class="uc">Input</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs108', 287)" onmouseover="showTip(event, 'fs108', 287)" class="id">t1</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs112', 288)" onmouseover="showTip(event, 'fs112', 288)" class="mv">e1</span><span class="pn">)</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs34', 289)" onmouseover="showTip(event, 'fs34', 289)" class="uc">Input</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs109', 290)" onmouseover="showTip(event, 'fs109', 290)" class="id">t2</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs113', 291)" onmouseover="showTip(event, 'fs113', 291)" class="mv">e2</span><span class="pn">)</span> <span class="k">-></span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs108', 292)" onmouseover="showTip(event, 'fs108', 292)" class="id">t1</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs109', 293)" onmouseover="showTip(event, 'fs109', 293)" class="id">t2</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs44', 294)" onmouseover="showTip(event, 'fs44', 294)" class="uc">EventUI</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs96', 295)" onmouseover="showTip(event, 'fs96', 295)" class="fn">update</span> <span onmouseout="hideTip(event, 'fs112', 296)" onmouseover="showTip(event, 'fs112', 296)" class="mv">e1</span> <span onmouseout="hideTip(event, 'fs113', 297)" onmouseover="showTip(event, 'fs113', 297)" class="mv">e2</span><span class="pn">)</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs105', 298)" onmouseover="showTip(event, 'fs105', 298)" class="id">diffs</span>
<span class="k">else</span> <span onmouseout="hideTip(event, 'fs44', 299)" onmouseover="showTip(event, 'fs44', 299)" class="uc">EventUI</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs96', 300)" onmouseover="showTip(event, 'fs96', 300)" class="fn">update</span> <span onmouseout="hideTip(event, 'fs112', 301)" onmouseover="showTip(event, 'fs112', 301)" class="mv">e1</span> <span onmouseout="hideTip(event, 'fs113', 302)" onmouseover="showTip(event, 'fs113', 302)" class="mv">e2</span><span class="pn">)</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs41', 303)" onmouseover="showTip(event, 'fs41', 303)" class="uc">UpdateUI</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs103', 304)" onmouseover="showTip(event, 'fs103', 304)" class="id">path</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs102', 305)" onmouseover="showTip(event, 'fs102', 305)" class="id">ui2</span><span class="pn">)</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs105', 306)" onmouseover="showTip(event, 'fs105', 306)" class="id">diffs</span>
<span class="pn">|</span><span onmouseout="hideTip(event, 'fs35', 307)" onmouseover="showTip(event, 'fs35', 307)" class="uc">Button</span> <span class="id">_</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs35', 308)" onmouseover="showTip(event, 'fs35', 308)" class="uc">Button</span> <span class="id">_</span> <span class="pn">|</span><span onmouseout="hideTip(event, 'fs34', 309)" onmouseover="showTip(event, 'fs34', 309)" class="uc">Input</span> <span class="id">_</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs34', 310)" onmouseover="showTip(event, 'fs34', 310)" class="uc">Input</span> <span class="id">_</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs41', 311)" onmouseover="showTip(event, 'fs41', 311)" class="uc">UpdateUI</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs103', 312)" onmouseover="showTip(event, 'fs103', 312)" class="id">path</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs102', 313)" onmouseover="showTip(event, 'fs102', 313)" class="id">ui2</span><span class="pn">)</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs105', 314)" onmouseover="showTip(event, 'fs105', 314)" class="id">diffs</span>
<span class="pn">|</span><span onmouseout="hideTip(event, 'fs36', 315)" onmouseover="showTip(event, 'fs36', 315)" class="uc">Div</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs114', 316)" onmouseover="showTip(event, 'fs114', 316)" class="id">l1</span><span class="pn">,</span><span class="id">_</span><span class="pn">)</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs36', 317)" onmouseover="showTip(event, 'fs36', 317)" class="uc">Div</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs115', 318)" onmouseover="showTip(event, 'fs115', 318)" class="id">l2</span><span class="pn">,</span><span class="id">_</span><span class="pn">)</span> <span class="k">when</span> <span onmouseout="hideTip(event, 'fs114', 319)" onmouseover="showTip(event, 'fs114', 319)" class="id">l1</span><span class="o"><></span><span onmouseout="hideTip(event, 'fs115', 320)" onmouseover="showTip(event, 'fs115', 320)" class="id">l2</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs42', 321)" onmouseover="showTip(event, 'fs42', 321)" class="uc">ReplaceUI</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs103', 322)" onmouseover="showTip(event, 'fs103', 322)" class="id">path</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs102', 323)" onmouseover="showTip(event, 'fs102', 323)" class="id">ui2</span><span class="pn">)</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs105', 324)" onmouseover="showTip(event, 'fs105', 324)" class="id">diffs</span>
<span class="pn">|</span><span onmouseout="hideTip(event, 'fs36', 325)" onmouseover="showTip(event, 'fs36', 325)" class="uc">Div</span> <span class="pn">(</span><span class="id">_</span><span class="pn">,</span><span class="pn">[</span><span class="pn">]</span><span class="pn">)</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs36', 326)" onmouseover="showTip(event, 'fs36', 326)" class="uc">Div</span> <span class="pn">(</span><span class="id">_</span><span class="pn">,</span><span class="pn">[</span><span class="pn">]</span><span class="pn">)</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs105', 327)" onmouseover="showTip(event, 'fs105', 327)" class="id">diffs</span>
<span class="pn">|</span><span onmouseout="hideTip(event, 'fs36', 328)" onmouseover="showTip(event, 'fs36', 328)" class="uc">Div</span> <span class="pn">(</span><span class="id">_</span><span class="pn">,</span><span class="pn">[</span><span class="pn">]</span><span class="pn">)</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs36', 329)" onmouseover="showTip(event, 'fs36', 329)" class="uc">Div</span> <span class="pn">(</span><span class="id">_</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs116', 330)" onmouseover="showTip(event, 'fs116', 330)" class="id">l</span><span class="pn">)</span> <span class="k">-></span>
<span onmouseout="hideTip(event, 'fs82', 331)" onmouseover="showTip(event, 'fs82', 331)" class="m">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs117', 332)" onmouseover="showTip(event, 'fs117', 332)" class="id">fold</span> <span class="pn">(</span><span class="k">fun</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs18', 333)" onmouseover="showTip(event, 'fs18', 333)" class="id">i</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs105', 334)" onmouseover="showTip(event, 'fs105', 334)" class="id">diffs</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs118', 335)" onmouseover="showTip(event, 'fs118', 335)" class="id">ui</span><span class="k">-></span><span onmouseout="hideTip(event, 'fs18', 336)" onmouseover="showTip(event, 'fs18', 336)" class="id">i</span><span class="o">+</span><span class="n">1</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs39', 337)" onmouseover="showTip(event, 'fs39', 337)" class="uc">InsertUI</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs18', 338)" onmouseover="showTip(event, 'fs18', 338)" class="id">i</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs103', 339)" onmouseover="showTip(event, 'fs103', 339)" class="id">path</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs118', 340)" onmouseover="showTip(event, 'fs118', 340)" class="id">ui</span><span class="pn">)</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs105', 341)" onmouseover="showTip(event, 'fs105', 341)" class="id">diffs</span><span class="pn">)</span>
<span class="pn">(</span><span onmouseout="hideTip(event, 'fs104', 342)" onmouseover="showTip(event, 'fs104', 342)" class="id">index</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs105', 343)" onmouseover="showTip(event, 'fs105', 343)" class="id">diffs</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs116', 344)" onmouseover="showTip(event, 'fs116', 344)" class="id">l</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs119', 345)" onmouseover="showTip(event, 'fs119', 345)" class="fn">snd</span>
<span class="pn">|</span><span onmouseout="hideTip(event, 'fs36', 346)" onmouseover="showTip(event, 'fs36', 346)" class="uc">Div</span> <span class="pn">(</span><span class="id">_</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs116', 347)" onmouseover="showTip(event, 'fs116', 347)" class="id">l</span><span class="pn">)</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs36', 348)" onmouseover="showTip(event, 'fs36', 348)" class="uc">Div</span> <span class="pn">(</span><span class="id">_</span><span class="pn">,</span><span class="pn">[</span><span class="pn">]</span><span class="pn">)</span> <span class="k">-></span>
<span onmouseout="hideTip(event, 'fs82', 349)" onmouseover="showTip(event, 'fs82', 349)" class="m">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs117', 350)" onmouseover="showTip(event, 'fs117', 350)" class="id">fold</span> <span class="pn">(</span><span class="k">fun</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs18', 351)" onmouseover="showTip(event, 'fs18', 351)" class="id">i</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs105', 352)" onmouseover="showTip(event, 'fs105', 352)" class="id">diffs</span><span class="pn">)</span> <span class="id">_</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs18', 353)" onmouseover="showTip(event, 'fs18', 353)" class="id">i</span><span class="o">+</span><span class="n">1</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs43', 354)" onmouseover="showTip(event, 'fs43', 354)" class="uc">RemoveUI</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs18', 355)" onmouseover="showTip(event, 'fs18', 355)" class="id">i</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs103', 356)" onmouseover="showTip(event, 'fs103', 356)" class="id">path</span><span class="pn">)</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs105', 357)" onmouseover="showTip(event, 'fs105', 357)" class="id">diffs</span><span class="pn">)</span>
<span class="pn">(</span><span onmouseout="hideTip(event, 'fs104', 358)" onmouseover="showTip(event, 'fs104', 358)" class="id">index</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs105', 359)" onmouseover="showTip(event, 'fs105', 359)" class="id">diffs</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs116', 360)" onmouseover="showTip(event, 'fs116', 360)" class="id">l</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs119', 361)" onmouseover="showTip(event, 'fs119', 361)" class="fn">snd</span>
<span class="pn">|</span><span onmouseout="hideTip(event, 'fs36', 362)" onmouseover="showTip(event, 'fs36', 362)" class="uc">Div</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs120', 363)" onmouseover="showTip(event, 'fs120', 363)" class="id">l</span><span class="pn">,</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs121', 364)" onmouseover="showTip(event, 'fs121', 364)" class="id">h1</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs122', 365)" onmouseover="showTip(event, 'fs122', 365)" class="id">t1</span><span class="pn">)</span><span class="pn">)</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs36', 366)" onmouseover="showTip(event, 'fs36', 366)" class="uc">Div</span> <span class="pn">(</span><span class="id">_</span><span class="pn">,</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs123', 367)" onmouseover="showTip(event, 'fs123', 367)" class="id">h2</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs124', 368)" onmouseover="showTip(event, 'fs124', 368)" class="id">t2</span><span class="pn">)</span><span class="pn">)</span>
<span class="k">when</span> <span onmouseout="hideTip(event, 'fs106', 369)" onmouseover="showTip(event, 'fs106', 369)" class="m">LanguagePrimitives</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs107', 370)" onmouseover="showTip(event, 'fs107', 370)" class="id">PhysicalEquality</span> <span onmouseout="hideTip(event, 'fs121', 371)" onmouseover="showTip(event, 'fs121', 371)" class="id">h1</span> <span onmouseout="hideTip(event, 'fs123', 372)" onmouseover="showTip(event, 'fs123', 372)" class="id">h2</span> <span class="k">-></span>
<span onmouseout="hideTip(event, 'fs100', 373)" onmouseover="showTip(event, 'fs100', 373)" class="fn">diff</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs36', 374)" onmouseover="showTip(event, 'fs36', 374)" class="uc">Div</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs120', 375)" onmouseover="showTip(event, 'fs120', 375)" class="id">l</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs122', 376)" onmouseover="showTip(event, 'fs122', 376)" class="id">t1</span><span class="pn">)</span><span class="pn">)</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs36', 377)" onmouseover="showTip(event, 'fs36', 377)" class="uc">Div</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs120', 378)" onmouseover="showTip(event, 'fs120', 378)" class="id">l</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs124', 379)" onmouseover="showTip(event, 'fs124', 379)" class="id">t2</span><span class="pn">)</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs103', 380)" onmouseover="showTip(event, 'fs103', 380)" class="id">path</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs104', 381)" onmouseover="showTip(event, 'fs104', 381)" class="id">index</span><span class="o">+</span><span class="n">1</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs105', 382)" onmouseover="showTip(event, 'fs105', 382)" class="id">diffs</span>
<span class="pn">|</span><span onmouseout="hideTip(event, 'fs36', 383)" onmouseover="showTip(event, 'fs36', 383)" class="uc">Div</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs120', 384)" onmouseover="showTip(event, 'fs120', 384)" class="id">l</span><span class="pn">,</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs121', 385)" onmouseover="showTip(event, 'fs121', 385)" class="id">h1</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs122', 386)" onmouseover="showTip(event, 'fs122', 386)" class="id">t1</span><span class="pn">)</span><span class="pn">)</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs36', 387)" onmouseover="showTip(event, 'fs36', 387)" class="uc">Div</span> <span class="pn">(</span><span class="id">_</span><span class="pn">,</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs123', 388)" onmouseover="showTip(event, 'fs123', 388)" class="id">h2</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs125', 389)" onmouseover="showTip(event, 'fs125', 389)" class="id">h3</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs124', 390)" onmouseover="showTip(event, 'fs124', 390)" class="id">t2</span><span class="pn">)</span><span class="pn">)</span>
<span class="k">when</span> <span onmouseout="hideTip(event, 'fs106', 391)" onmouseover="showTip(event, 'fs106', 391)" class="m">LanguagePrimitives</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs107', 392)" onmouseover="showTip(event, 'fs107', 392)" class="id">PhysicalEquality</span> <span onmouseout="hideTip(event, 'fs121', 393)" onmouseover="showTip(event, 'fs121', 393)" class="id">h1</span> <span onmouseout="hideTip(event, 'fs125', 394)" onmouseover="showTip(event, 'fs125', 394)" class="id">h3</span> <span class="k">-></span>
<span onmouseout="hideTip(event, 'fs100', 395)" onmouseover="showTip(event, 'fs100', 395)" class="fn">diff</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs36', 396)" onmouseover="showTip(event, 'fs36', 396)" class="uc">Div</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs120', 397)" onmouseover="showTip(event, 'fs120', 397)" class="id">l</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs122', 398)" onmouseover="showTip(event, 'fs122', 398)" class="id">t1</span><span class="pn">)</span><span class="pn">)</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs36', 399)" onmouseover="showTip(event, 'fs36', 399)" class="uc">Div</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs120', 400)" onmouseover="showTip(event, 'fs120', 400)" class="id">l</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs124', 401)" onmouseover="showTip(event, 'fs124', 401)" class="id">t2</span><span class="pn">)</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs103', 402)" onmouseover="showTip(event, 'fs103', 402)" class="id">path</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs104', 403)" onmouseover="showTip(event, 'fs104', 403)" class="id">index</span><span class="o">+</span><span class="n">1</span><span class="pn">)</span>
<span class="pn">(</span><span onmouseout="hideTip(event, 'fs39', 404)" onmouseover="showTip(event, 'fs39', 404)" class="uc">InsertUI</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs104', 405)" onmouseover="showTip(event, 'fs104', 405)" class="id">index</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs103', 406)" onmouseover="showTip(event, 'fs103', 406)" class="id">path</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs123', 407)" onmouseover="showTip(event, 'fs123', 407)" class="id">h2</span><span class="pn">)</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs105', 408)" onmouseover="showTip(event, 'fs105', 408)" class="id">diffs</span><span class="pn">)</span>
<span class="pn">|</span><span onmouseout="hideTip(event, 'fs36', 409)" onmouseover="showTip(event, 'fs36', 409)" class="uc">Div</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs120', 410)" onmouseover="showTip(event, 'fs120', 410)" class="id">l</span><span class="pn">,</span><span class="pn">(</span><span class="id">_</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs123', 411)" onmouseover="showTip(event, 'fs123', 411)" class="id">h2</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs122', 412)" onmouseover="showTip(event, 'fs122', 412)" class="id">t1</span><span class="pn">)</span><span class="pn">)</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs36', 413)" onmouseover="showTip(event, 'fs36', 413)" class="uc">Div</span> <span class="pn">(</span><span class="id">_</span><span class="pn">,</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs125', 414)" onmouseover="showTip(event, 'fs125', 414)" class="id">h3</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs124', 415)" onmouseover="showTip(event, 'fs124', 415)" class="id">t2</span><span class="pn">)</span><span class="pn">)</span>
<span class="k">when</span> <span onmouseout="hideTip(event, 'fs106', 416)" onmouseover="showTip(event, 'fs106', 416)" class="m">LanguagePrimitives</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs107', 417)" onmouseover="showTip(event, 'fs107', 417)" class="id">PhysicalEquality</span> <span onmouseout="hideTip(event, 'fs123', 418)" onmouseover="showTip(event, 'fs123', 418)" class="id">h2</span> <span onmouseout="hideTip(event, 'fs125', 419)" onmouseover="showTip(event, 'fs125', 419)" class="id">h3</span> <span class="k">-></span>
<span onmouseout="hideTip(event, 'fs100', 420)" onmouseover="showTip(event, 'fs100', 420)" class="fn">diff</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs36', 421)" onmouseover="showTip(event, 'fs36', 421)" class="uc">Div</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs120', 422)" onmouseover="showTip(event, 'fs120', 422)" class="id">l</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs122', 423)" onmouseover="showTip(event, 'fs122', 423)" class="id">t1</span><span class="pn">)</span><span class="pn">)</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs36', 424)" onmouseover="showTip(event, 'fs36', 424)" class="uc">Div</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs120', 425)" onmouseover="showTip(event, 'fs120', 425)" class="id">l</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs124', 426)" onmouseover="showTip(event, 'fs124', 426)" class="id">t2</span><span class="pn">)</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs103', 427)" onmouseover="showTip(event, 'fs103', 427)" class="id">path</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs104', 428)" onmouseover="showTip(event, 'fs104', 428)" class="id">index</span><span class="o">+</span><span class="n">1</span><span class="pn">)</span>
<span class="pn">(</span><span onmouseout="hideTip(event, 'fs43', 429)" onmouseover="showTip(event, 'fs43', 429)" class="uc">RemoveUI</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs104', 430)" onmouseover="showTip(event, 'fs104', 430)" class="id">index</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs103', 431)" onmouseover="showTip(event, 'fs103', 431)" class="id">path</span><span class="pn">)</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs105', 432)" onmouseover="showTip(event, 'fs105', 432)" class="id">diffs</span><span class="pn">)</span>
<span class="pn">|</span><span onmouseout="hideTip(event, 'fs36', 433)" onmouseover="showTip(event, 'fs36', 433)" class="uc">Div</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs120', 434)" onmouseover="showTip(event, 'fs120', 434)" class="id">l</span><span class="pn">,</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs121', 435)" onmouseover="showTip(event, 'fs121', 435)" class="id">h1</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs122', 436)" onmouseover="showTip(event, 'fs122', 436)" class="id">t1</span><span class="pn">)</span><span class="pn">)</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs36', 437)" onmouseover="showTip(event, 'fs36', 437)" class="uc">Div</span> <span class="pn">(</span><span class="id">_</span><span class="pn">,</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs123', 438)" onmouseover="showTip(event, 'fs123', 438)" class="id">h2</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs124', 439)" onmouseover="showTip(event, 'fs124', 439)" class="id">t2</span><span class="pn">)</span><span class="pn">)</span> <span class="k">-></span>
<span onmouseout="hideTip(event, 'fs100', 440)" onmouseover="showTip(event, 'fs100', 440)" class="fn">diff</span> <span onmouseout="hideTip(event, 'fs121', 441)" onmouseover="showTip(event, 'fs121', 441)" class="id">h1</span> <span onmouseout="hideTip(event, 'fs123', 442)" onmouseover="showTip(event, 'fs123', 442)" class="id">h2</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs104', 443)" onmouseover="showTip(event, 'fs104', 443)" class="id">index</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs103', 444)" onmouseover="showTip(event, 'fs103', 444)" class="id">path</span><span class="pn">)</span> <span class="n">0</span> <span onmouseout="hideTip(event, 'fs105', 445)" onmouseover="showTip(event, 'fs105', 445)" class="id">diffs</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs100', 446)" onmouseover="showTip(event, 'fs100', 446)" class="fn">diff</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs36', 447)" onmouseover="showTip(event, 'fs36', 447)" class="uc">Div</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs120', 448)" onmouseover="showTip(event, 'fs120', 448)" class="id">l</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs122', 449)" onmouseover="showTip(event, 'fs122', 449)" class="id">t1</span><span class="pn">)</span><span class="pn">)</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs36', 450)" onmouseover="showTip(event, 'fs36', 450)" class="uc">Div</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs120', 451)" onmouseover="showTip(event, 'fs120', 451)" class="id">l</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs124', 452)" onmouseover="showTip(event, 'fs124', 452)" class="id">t2</span><span class="pn">)</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs103', 453)" onmouseover="showTip(event, 'fs103', 453)" class="id">path</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs104', 454)" onmouseover="showTip(event, 'fs104', 454)" class="id">index</span><span class="o">+</span><span class="n">1</span><span class="pn">)</span>
<span class="pn">|</span><span class="id">_</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs42', 455)" onmouseover="showTip(event, 'fs42', 455)" class="uc">ReplaceUI</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs103', 456)" onmouseover="showTip(event, 'fs103', 456)" class="id">path</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs102', 457)" onmouseover="showTip(event, 'fs102', 457)" class="id">ui2</span><span class="pn">)</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs105', 458)" onmouseover="showTip(event, 'fs105', 458)" class="id">diffs</span>
<span onmouseout="hideTip(event, 'fs100', 459)" onmouseover="showTip(event, 'fs100', 459)" class="fn">diff</span> <span onmouseout="hideTip(event, 'fs101', 460)" onmouseover="showTip(event, 'fs101', 460)" class="id">ui1</span><span class="pn">.</span><span class="id">UI</span> <span onmouseout="hideTip(event, 'fs91', 461)" onmouseover="showTip(event, 'fs91', 461)" class="id">ui2</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs84', 462)" onmouseover="showTip(event, 'fs84', 462)" class="id">UI</span> <span class="pn">[</span><span class="pn">]</span> <span class="n">0</span> <span class="pn">[</span><span class="pn">]</span>
<span class="c">/// Returns a UI application from a UI model, update and view.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs126', 463)" onmouseover="showTip(event, 'fs126', 463)" class="fn">app</span> <span onmouseout="hideTip(event, 'fs127', 464)" onmouseover="showTip(event, 'fs127', 464)" class="id">model</span> <span onmouseout="hideTip(event, 'fs128', 465)" onmouseover="showTip(event, 'fs128', 465)" class="fn">update</span> <span onmouseout="hideTip(event, 'fs129', 466)" onmouseover="showTip(event, 'fs129', 466)" class="fn">view</span> <span class="o">=</span> <span class="pn">{</span><span class="id">Model</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs127', 467)" onmouseover="showTip(event, 'fs127', 467)" class="id">model</span><span class="pn">;</span><span class="fn">Update</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs128', 468)" onmouseover="showTip(event, 'fs128', 468)" class="fn">update</span><span class="pn">;</span><span class="fn">View</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs129', 469)" onmouseover="showTip(event, 'fs129', 469)" class="fn">view</span><span class="pn">}</span>
<span class="c">/// Runs a UI application given a native UI.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs130', 470)" onmouseover="showTip(event, 'fs130', 470)" class="fn">run</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs131', 471)" onmouseover="showTip(event, 'fs131', 471)" class="id">nativeUI</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs52', 472)" onmouseover="showTip(event, 'fs52', 472)" class="if">INativeUI</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs132', 473)" onmouseover="showTip(event, 'fs132', 473)" class="id">app</span> <span class="o">=</span>
<span class="k">let</span> <span class="k">rec</span> <span onmouseout="hideTip(event, 'fs133', 474)" onmouseover="showTip(event, 'fs133', 474)" class="fn">handle</span> <span onmouseout="hideTip(event, 'fs134', 475)" onmouseover="showTip(event, 'fs134', 475)" class="id">model</span> <span onmouseout="hideTip(event, 'fs77', 476)" onmouseover="showTip(event, 'fs77', 476)" class="id">ui</span> <span onmouseout="hideTip(event, 'fs75', 477)" onmouseover="showTip(event, 'fs75', 477)" class="id">msg</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs135', 478)" onmouseover="showTip(event, 'fs135', 478)" class="id">newModel</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs132', 479)" onmouseover="showTip(event, 'fs132', 479)" class="fn">app</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs136', 480)" onmouseover="showTip(event, 'fs136', 480)" class="id">Update</span> <span onmouseout="hideTip(event, 'fs75', 481)" onmouseover="showTip(event, 'fs75', 481)" class="id">msg</span> <span onmouseout="hideTip(event, 'fs134', 482)" onmouseover="showTip(event, 'fs134', 482)" class="id">model</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs137', 483)" onmouseover="showTip(event, 'fs137', 483)" class="id">newUI</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs132', 484)" onmouseover="showTip(event, 'fs132', 484)" class="fn">app</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs138', 485)" onmouseover="showTip(event, 'fs138', 485)" class="id">View</span> <span onmouseout="hideTip(event, 'fs135', 486)" onmouseover="showTip(event, 'fs135', 486)" class="id">newModel</span>
<span onmouseout="hideTip(event, 'fs137', 487)" onmouseover="showTip(event, 'fs137', 487)" class="mv">newUI</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs78', 488)" onmouseover="showTip(event, 'fs78', 488)" class="id">Event</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs133', 489)" onmouseover="showTip(event, 'fs133', 489)" class="fn">handle</span> <span onmouseout="hideTip(event, 'fs135', 490)" onmouseover="showTip(event, 'fs135', 490)" class="id">newModel</span> <span onmouseout="hideTip(event, 'fs137', 491)" onmouseover="showTip(event, 'fs137', 491)" class="id">newUI</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs139', 492)" onmouseover="showTip(event, 'fs139', 492)" class="id">diff</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs139', 493)" onmouseover="showTip(event, 'fs139', 493)" class="fn">diff</span> <span onmouseout="hideTip(event, 'fs77', 494)" onmouseover="showTip(event, 'fs77', 494)" class="id">ui</span> <span onmouseout="hideTip(event, 'fs137', 495)" onmouseover="showTip(event, 'fs137', 495)" class="id">newUI</span>
<span onmouseout="hideTip(event, 'fs82', 496)" onmouseover="showTip(event, 'fs82', 496)" class="m">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs87', 497)" onmouseover="showTip(event, 'fs87', 497)" class="id">iter</span> <span class="pn">(</span><span class="k">function</span> <span class="pn">|</span><span onmouseout="hideTip(event, 'fs44', 498)" onmouseover="showTip(event, 'fs44', 498)" class="uc">EventUI</span> <span onmouseout="hideTip(event, 'fs140', 499)" onmouseover="showTip(event, 'fs140', 499)" class="fn">f</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs140', 500)" onmouseover="showTip(event, 'fs140', 500)" class="fn">f</span><span class="pn">(</span><span class="pn">)</span> <span class="pn">|</span><span class="id">_</span><span class="k">-></span> <span class="pn">(</span><span class="pn">)</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs139', 501)" onmouseover="showTip(event, 'fs139', 501)" class="id">diff</span>
<span onmouseout="hideTip(event, 'fs131', 502)" onmouseover="showTip(event, 'fs131', 502)" class="fn">nativeUI</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs141', 503)" onmouseover="showTip(event, 'fs141', 503)" class="id">Send</span> <span onmouseout="hideTip(event, 'fs139', 504)" onmouseover="showTip(event, 'fs139', 504)" class="id">diff</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs77', 505)" onmouseover="showTip(event, 'fs77', 505)" class="id">ui</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs132', 506)" onmouseover="showTip(event, 'fs132', 506)" class="fn">app</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs138', 507)" onmouseover="showTip(event, 'fs138', 507)" class="id">View</span> <span onmouseout="hideTip(event, 'fs132', 508)" onmouseover="showTip(event, 'fs132', 508)" class="id">app</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs142', 509)" onmouseover="showTip(event, 'fs142', 509)" class="id">Model</span>
<span onmouseout="hideTip(event, 'fs77', 510)" onmouseover="showTip(event, 'fs77', 510)" class="mv">ui</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs78', 511)" onmouseover="showTip(event, 'fs78', 511)" class="id">Event</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs133', 512)" onmouseover="showTip(event, 'fs133', 512)" class="fn">handle</span> <span onmouseout="hideTip(event, 'fs132', 513)" onmouseover="showTip(event, 'fs132', 513)" class="id">app</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs142', 514)" onmouseover="showTip(event, 'fs142', 514)" class="id">Model</span> <span onmouseout="hideTip(event, 'fs77', 515)" onmouseover="showTip(event, 'fs77', 515)" class="id">ui</span>
<span onmouseout="hideTip(event, 'fs131', 516)" onmouseover="showTip(event, 'fs131', 516)" class="fn">nativeUI</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs141', 517)" onmouseover="showTip(event, 'fs141', 517)" class="id">Send</span> <span class="pn">[</span><span onmouseout="hideTip(event, 'fs39', 518)" onmouseover="showTip(event, 'fs39', 518)" class="uc">InsertUI</span><span class="pn">(</span><span class="pn">[</span><span class="pn">]</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs77', 519)" onmouseover="showTip(event, 'fs77', 519)" class="id">ui</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs84', 520)" onmouseover="showTip(event, 'fs84', 520)" class="id">UI</span><span class="pn">)</span><span class="pn">]</span>
</code></pre>
<h2><a name="Example-UI-applications" class="anchor" href="#Example-UI-applications">Example UI applications</a></h2>
<p>The UI application pattern has four main parts:</p>
<ol>
<li><strong>Model</strong> - the state of the application.</li>
<li><strong>Msg</strong> - an update message.</li>
<li><strong>Update</strong> - a function that updates the state.</li>
<li><strong>View</strong> - a function that views state as a UI.</li>
</ol>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="k">module</span> <span class="m">Counter</span> <span class="o">=</span>
<span class="k">type</span> <span onmouseout="hideTip(event, 'fs143', 521)" onmouseover="showTip(event, 'fs143', 521)" class="vt">Model</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs40', 522)" onmouseover="showTip(event, 'fs40', 522)" class="vt">int</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs144', 523)" onmouseover="showTip(event, 'fs144', 523)" class="fn">init</span> <span onmouseout="hideTip(event, 'fs145', 524)" onmouseover="showTip(event, 'fs145', 524)" class="id">i</span> <span class="pn">:</span> <span onmouseout="hideTip(event, 'fs143', 525)" onmouseover="showTip(event, 'fs143', 525)" class="vt">Model</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs145', 526)" onmouseover="showTip(event, 'fs145', 526)" class="id">i</span>
<span class="k">type</span> <span onmouseout="hideTip(event, 'fs146', 527)" onmouseover="showTip(event, 'fs146', 527)" class="rt">Msg</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs147', 528)" onmouseover="showTip(event, 'fs147', 528)" class="uc">Increment</span> <span class="pn">|</span> <span onmouseout="hideTip(event, 'fs148', 529)" onmouseover="showTip(event, 'fs148', 529)" class="uc">Decrement</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs149', 530)" onmouseover="showTip(event, 'fs149', 530)" class="fn">update</span> <span onmouseout="hideTip(event, 'fs150', 531)" onmouseover="showTip(event, 'fs150', 531)" class="id">msg</span> <span onmouseout="hideTip(event, 'fs151', 532)" onmouseover="showTip(event, 'fs151', 532)" class="id">model</span> <span class="o">=</span>
<span class="k">match</span> <span onmouseout="hideTip(event, 'fs150', 533)" onmouseover="showTip(event, 'fs150', 533)" class="id">msg</span> <span class="k">with</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs147', 534)" onmouseover="showTip(event, 'fs147', 534)" class="uc">Increment</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs151', 535)" onmouseover="showTip(event, 'fs151', 535)" class="id">model</span><span class="o">+</span><span class="n">1</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs148', 536)" onmouseover="showTip(event, 'fs148', 536)" class="uc">Decrement</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs151', 537)" onmouseover="showTip(event, 'fs151', 537)" class="id">model</span><span class="o">-</span><span class="n">1</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs152', 538)" onmouseover="showTip(event, 'fs152', 538)" class="fn">view</span> <span onmouseout="hideTip(event, 'fs153', 539)" onmouseover="showTip(event, 'fs153', 539)" class="id">model</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs154', 540)" onmouseover="showTip(event, 'fs154', 540)" class="m">UI</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs79', 541)" onmouseover="showTip(event, 'fs79', 541)" class="id">div</span> <span onmouseout="hideTip(event, 'fs29', 542)" onmouseover="showTip(event, 'fs29', 542)" class="uc">Horizontal</span> <span class="pn">[</span>
<span onmouseout="hideTip(event, 'fs154', 543)" onmouseover="showTip(event, 'fs154', 543)" class="m">UI</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs74', 544)" onmouseover="showTip(event, 'fs74', 544)" class="id">button</span> <span class="s">"+"</span> <span onmouseout="hideTip(event, 'fs147', 545)" onmouseover="showTip(event, 'fs147', 545)" class="uc">Increment</span>
<span onmouseout="hideTip(event, 'fs154', 546)" onmouseover="showTip(event, 'fs154', 546)" class="m">UI</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs74', 547)" onmouseover="showTip(event, 'fs74', 547)" class="id">button</span> <span class="s">"-"</span> <span onmouseout="hideTip(event, 'fs148', 548)" onmouseover="showTip(event, 'fs148', 548)" class="uc">Decrement</span>
<span onmouseout="hideTip(event, 'fs154', 549)" onmouseover="showTip(event, 'fs154', 549)" class="m">UI</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs65', 550)" onmouseover="showTip(event, 'fs65', 550)" class="id">text</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs33', 551)" onmouseover="showTip(event, 'fs33', 551)" class="fn">string</span> <span onmouseout="hideTip(event, 'fs153', 552)" onmouseover="showTip(event, 'fs153', 552)" class="id">model</span><span class="pn">)</span>
<span class="pn">]</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs155', 553)" onmouseover="showTip(event, 'fs155', 553)" class="fn">app</span> <span onmouseout="hideTip(event, 'fs145', 554)" onmouseover="showTip(event, 'fs145', 554)" class="id">i</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs154', 555)" onmouseover="showTip(event, 'fs154', 555)" class="m">UI</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs126', 556)" onmouseover="showTip(event, 'fs126', 556)" class="id">app</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs144', 557)" onmouseover="showTip(event, 'fs144', 557)" class="fn">init</span> <span onmouseout="hideTip(event, 'fs145', 558)" onmouseover="showTip(event, 'fs145', 558)" class="id">i</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs149', 559)" onmouseover="showTip(event, 'fs149', 559)" class="fn">update</span> <span onmouseout="hideTip(event, 'fs152', 560)" onmouseover="showTip(event, 'fs152', 560)" class="fn">view</span>
<span class="k">module</span> <span class="m">CounterPair</span> <span class="o">=</span>
<span class="k">type</span> <span onmouseout="hideTip(event, 'fs156', 561)" onmouseover="showTip(event, 'fs156', 561)" class="rt">Model</span> <span class="o">=</span> <span class="pn">{</span><span onmouseout="hideTip(event, 'fs157', 562)" onmouseover="showTip(event, 'fs157', 562)" class="id">Top</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs158', 563)" onmouseover="showTip(event, 'fs158', 563)" class="m">Counter</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs143', 564)" onmouseover="showTip(event, 'fs143', 564)" class="id">Model</span><span class="pn">;</span><span onmouseout="hideTip(event, 'fs159', 565)" onmouseover="showTip(event, 'fs159', 565)" class="id">Bottom</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs158', 566)" onmouseover="showTip(event, 'fs158', 566)" class="m">Counter</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs143', 567)" onmouseover="showTip(event, 'fs143', 567)" class="id">Model</span><span class="pn">}</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs160', 568)" onmouseover="showTip(event, 'fs160', 568)" class="fn">init</span> <span onmouseout="hideTip(event, 'fs161', 569)" onmouseover="showTip(event, 'fs161', 569)" class="id">top</span> <span onmouseout="hideTip(event, 'fs162', 570)" onmouseover="showTip(event, 'fs162', 570)" class="id">bottom</span> <span class="o">=</span>
<span class="pn">{</span><span class="id">Top</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs158', 571)" onmouseover="showTip(event, 'fs158', 571)" class="m">Counter</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs163', 572)" onmouseover="showTip(event, 'fs163', 572)" class="id">init</span> <span onmouseout="hideTip(event, 'fs161', 573)" onmouseover="showTip(event, 'fs161', 573)" class="id">top</span><span class="pn">;</span><span class="id">Bottom</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs158', 574)" onmouseover="showTip(event, 'fs158', 574)" class="m">Counter</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs163', 575)" onmouseover="showTip(event, 'fs163', 575)" class="id">init</span> <span onmouseout="hideTip(event, 'fs162', 576)" onmouseover="showTip(event, 'fs162', 576)" class="id">bottom</span><span class="pn">}</span>
<span class="k">type</span> <span onmouseout="hideTip(event, 'fs164', 577)" onmouseover="showTip(event, 'fs164', 577)" class="rt">Msg</span> <span class="o">=</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs165', 578)" onmouseover="showTip(event, 'fs165', 578)" class="uc">Reset</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs166', 579)" onmouseover="showTip(event, 'fs166', 579)" class="uc">Top</span> <span class="k">of</span> <span onmouseout="hideTip(event, 'fs158', 580)" onmouseover="showTip(event, 'fs158', 580)" class="m">Counter</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs146', 581)" onmouseover="showTip(event, 'fs146', 581)" class="id">Msg</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs167', 582)" onmouseover="showTip(event, 'fs167', 582)" class="uc">Bottom</span> <span class="k">of</span> <span onmouseout="hideTip(event, 'fs158', 583)" onmouseover="showTip(event, 'fs158', 583)" class="m">Counter</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs146', 584)" onmouseover="showTip(event, 'fs146', 584)" class="id">Msg</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs168', 585)" onmouseover="showTip(event, 'fs168', 585)" class="fn">update</span> <span onmouseout="hideTip(event, 'fs150', 586)" onmouseover="showTip(event, 'fs150', 586)" class="id">msg</span> <span onmouseout="hideTip(event, 'fs153', 587)" onmouseover="showTip(event, 'fs153', 587)" class="id">model</span> <span class="o">=</span>
<span class="k">match</span> <span onmouseout="hideTip(event, 'fs150', 588)" onmouseover="showTip(event, 'fs150', 588)" class="id">msg</span> <span class="k">with</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs165', 589)" onmouseover="showTip(event, 'fs165', 589)" class="uc">Reset</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs160', 590)" onmouseover="showTip(event, 'fs160', 590)" class="fn">init</span> <span class="n">0</span> <span class="n">0</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs166', 591)" onmouseover="showTip(event, 'fs166', 591)" class="uc">Top</span> <span onmouseout="hideTip(event, 'fs169', 592)" onmouseover="showTip(event, 'fs169', 592)" class="id">msg</span> <span class="k">-></span> <span class="pn">{</span><span onmouseout="hideTip(event, 'fs153', 593)" onmouseover="showTip(event, 'fs153', 593)" class="id">model</span> <span class="k">with</span> <span onmouseout="hideTip(event, 'fs166', 594)" onmouseover="showTip(event, 'fs166', 594)" class="id">Top</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs158', 595)" onmouseover="showTip(event, 'fs158', 595)" class="m">Counter</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs170', 596)" onmouseover="showTip(event, 'fs170', 596)" class="id">update</span> <span onmouseout="hideTip(event, 'fs169', 597)" onmouseover="showTip(event, 'fs169', 597)" class="id">msg</span> <span onmouseout="hideTip(event, 'fs153', 598)" onmouseover="showTip(event, 'fs153', 598)" class="id">model</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs157', 599)" onmouseover="showTip(event, 'fs157', 599)" class="id">Top</span><span class="pn">}</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs167', 600)" onmouseover="showTip(event, 'fs167', 600)" class="uc">Bottom</span> <span onmouseout="hideTip(event, 'fs169', 601)" onmouseover="showTip(event, 'fs169', 601)" class="id">msg</span> <span class="k">-></span> <span class="pn">{</span><span onmouseout="hideTip(event, 'fs153', 602)" onmouseover="showTip(event, 'fs153', 602)" class="id">model</span> <span class="k">with</span> <span onmouseout="hideTip(event, 'fs167', 603)" onmouseover="showTip(event, 'fs167', 603)" class="id">Bottom</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs158', 604)" onmouseover="showTip(event, 'fs158', 604)" class="m">Counter</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs170', 605)" onmouseover="showTip(event, 'fs170', 605)" class="id">update</span> <span onmouseout="hideTip(event, 'fs169', 606)" onmouseover="showTip(event, 'fs169', 606)" class="id">msg</span> <span onmouseout="hideTip(event, 'fs153', 607)" onmouseover="showTip(event, 'fs153', 607)" class="id">model</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs159', 608)" onmouseover="showTip(event, 'fs159', 608)" class="id">Bottom</span><span class="pn">}</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs152', 609)" onmouseover="showTip(event, 'fs152', 609)" class="fn">view</span> <span onmouseout="hideTip(event, 'fs153', 610)" onmouseover="showTip(event, 'fs153', 610)" class="id">model</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs154', 611)" onmouseover="showTip(event, 'fs154', 611)" class="m">UI</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs79', 612)" onmouseover="showTip(event, 'fs79', 612)" class="id">div</span> <span onmouseout="hideTip(event, 'fs30', 613)" onmouseover="showTip(event, 'fs30', 613)" class="uc">Vertical</span> <span class="pn">[</span>
<span onmouseout="hideTip(event, 'fs158', 614)" onmouseover="showTip(event, 'fs158', 614)" class="m">Counter</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs171', 615)" onmouseover="showTip(event, 'fs171', 615)" class="id">view</span> <span onmouseout="hideTip(event, 'fs153', 616)" onmouseover="showTip(event, 'fs153', 616)" class="id">model</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs157', 617)" onmouseover="showTip(event, 'fs157', 617)" class="id">Top</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs154', 618)" onmouseover="showTip(event, 'fs154', 618)" class="m">UI</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs89', 619)" onmouseover="showTip(event, 'fs89', 619)" class="id">map</span> <span onmouseout="hideTip(event, 'fs166', 620)" onmouseover="showTip(event, 'fs166', 620)" class="uc">Top</span>
<span onmouseout="hideTip(event, 'fs158', 621)" onmouseover="showTip(event, 'fs158', 621)" class="m">Counter</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs171', 622)" onmouseover="showTip(event, 'fs171', 622)" class="id">view</span> <span onmouseout="hideTip(event, 'fs153', 623)" onmouseover="showTip(event, 'fs153', 623)" class="id">model</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs159', 624)" onmouseover="showTip(event, 'fs159', 624)" class="id">Bottom</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs154', 625)" onmouseover="showTip(event, 'fs154', 625)" class="m">UI</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs89', 626)" onmouseover="showTip(event, 'fs89', 626)" class="id">map</span> <span onmouseout="hideTip(event, 'fs167', 627)" onmouseover="showTip(event, 'fs167', 627)" class="uc">Bottom</span>
<span class="pn">]</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs172', 628)" onmouseover="showTip(event, 'fs172', 628)" class="fn">app</span> <span onmouseout="hideTip(event, 'fs161', 629)" onmouseover="showTip(event, 'fs161', 629)" class="id">top</span> <span onmouseout="hideTip(event, 'fs162', 630)" onmouseover="showTip(event, 'fs162', 630)" class="id">bottom</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs154', 631)" onmouseover="showTip(event, 'fs154', 631)" class="m">UI</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs126', 632)" onmouseover="showTip(event, 'fs126', 632)" class="id">app</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs160', 633)" onmouseover="showTip(event, 'fs160', 633)" class="fn">init</span> <span onmouseout="hideTip(event, 'fs161', 634)" onmouseover="showTip(event, 'fs161', 634)" class="id">top</span> <span onmouseout="hideTip(event, 'fs162', 635)" onmouseover="showTip(event, 'fs162', 635)" class="id">bottom</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs168', 636)" onmouseover="showTip(event, 'fs168', 636)" class="fn">update</span> <span onmouseout="hideTip(event, 'fs152', 637)" onmouseover="showTip(event, 'fs152', 637)" class="fn">view</span>
<span class="k">module</span> <span onmouseout="hideTip(event, 'fs173', 638)" onmouseover="showTip(event, 'fs173', 638)" class="m">CounterList</span> <span class="o">=</span>
<span class="k">type</span> <span onmouseout="hideTip(event, 'fs174', 639)" onmouseover="showTip(event, 'fs174', 639)" class="rt">Model</span> <span class="o">=</span> <span class="pn">{</span><span onmouseout="hideTip(event, 'fs175', 640)" onmouseover="showTip(event, 'fs175', 640)" class="id">Counters</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs158', 641)" onmouseover="showTip(event, 'fs158', 641)" class="m">Counter</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs143', 642)" onmouseover="showTip(event, 'fs143', 642)" class="id">Model</span> <span onmouseout="hideTip(event, 'fs37', 643)" onmouseover="showTip(event, 'fs37', 643)" class="rt">list</span><span class="pn">}</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs176', 644)" onmouseover="showTip(event, 'fs176', 644)" class="id">init</span> <span class="o">=</span> <span class="pn">{</span><span class="id">Counters</span><span class="o">=</span><span class="pn">[</span><span class="pn">]</span><span class="pn">}</span>
<span class="k">type</span> <span onmouseout="hideTip(event, 'fs177', 645)" onmouseover="showTip(event, 'fs177', 645)" class="rt">Msg</span> <span class="o">=</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs178', 646)" onmouseover="showTip(event, 'fs178', 646)" class="uc">Insert</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs179', 647)" onmouseover="showTip(event, 'fs179', 647)" class="uc">Remove</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs180', 648)" onmouseover="showTip(event, 'fs180', 648)" class="uc">Modify</span> <span class="k">of</span> <span onmouseout="hideTip(event, 'fs40', 649)" onmouseover="showTip(event, 'fs40', 649)" class="vt">int</span> <span class="pn">*</span> <span onmouseout="hideTip(event, 'fs158', 650)" onmouseover="showTip(event, 'fs158', 650)" class="m">Counter</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs146', 651)" onmouseover="showTip(event, 'fs146', 651)" class="id">Msg</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs168', 652)" onmouseover="showTip(event, 'fs168', 652)" class="fn">update</span> <span onmouseout="hideTip(event, 'fs150', 653)" onmouseover="showTip(event, 'fs150', 653)" class="id">msg</span> <span onmouseout="hideTip(event, 'fs153', 654)" onmouseover="showTip(event, 'fs153', 654)" class="id">model</span> <span class="o">=</span>
<span class="k">match</span> <span onmouseout="hideTip(event, 'fs150', 655)" onmouseover="showTip(event, 'fs150', 655)" class="id">msg</span> <span class="k">with</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs178', 656)" onmouseover="showTip(event, 'fs178', 656)" class="uc">Insert</span> <span class="k">-></span> <span class="pn">{</span><span onmouseout="hideTip(event, 'fs153', 657)" onmouseover="showTip(event, 'fs153', 657)" class="id">model</span> <span class="k">with</span> <span class="id">Counters</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs158', 658)" onmouseover="showTip(event, 'fs158', 658)" class="m">Counter</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs163', 659)" onmouseover="showTip(event, 'fs163', 659)" class="id">init</span> <span class="n">0</span><span class="uc">::</span><span onmouseout="hideTip(event, 'fs153', 660)" onmouseover="showTip(event, 'fs153', 660)" class="id">model</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs175', 661)" onmouseover="showTip(event, 'fs175', 661)" class="id">Counters</span><span class="pn">}</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs179', 662)" onmouseover="showTip(event, 'fs179', 662)" class="uc">Remove</span> <span class="k">-></span> <span class="pn">{</span><span onmouseout="hideTip(event, 'fs153', 663)" onmouseover="showTip(event, 'fs153', 663)" class="id">model</span> <span class="k">with</span> <span class="id">Counters</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs82', 664)" onmouseover="showTip(event, 'fs82', 664)" class="m">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs24', 665)" onmouseover="showTip(event, 'fs24', 665)" class="id">tail</span> <span onmouseout="hideTip(event, 'fs153', 666)" onmouseover="showTip(event, 'fs153', 666)" class="id">model</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs175', 667)" onmouseover="showTip(event, 'fs175', 667)" class="id">Counters</span><span class="pn">}</span>
<span class="pn">|</span> <span onmouseout="hideTip(event, 'fs180', 668)" onmouseover="showTip(event, 'fs180', 668)" class="uc">Modify</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs18', 669)" onmouseover="showTip(event, 'fs18', 669)" class="id">i</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs169', 670)" onmouseover="showTip(event, 'fs169', 670)" class="id">msg</span><span class="pn">)</span> <span class="k">-></span>
<span class="pn">{</span><span onmouseout="hideTip(event, 'fs153', 671)" onmouseover="showTip(event, 'fs153', 671)" class="id">model</span> <span class="k">with</span> <span class="id">Counters</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs82', 672)" onmouseover="showTip(event, 'fs82', 672)" class="m">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs17', 673)" onmouseover="showTip(event, 'fs17', 673)" class="id">mapAt</span> <span onmouseout="hideTip(event, 'fs18', 674)" onmouseover="showTip(event, 'fs18', 674)" class="id">i</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs158', 675)" onmouseover="showTip(event, 'fs158', 675)" class="m">Counter</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs170', 676)" onmouseover="showTip(event, 'fs170', 676)" class="id">update</span> <span onmouseout="hideTip(event, 'fs169', 677)" onmouseover="showTip(event, 'fs169', 677)" class="id">msg</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs153', 678)" onmouseover="showTip(event, 'fs153', 678)" class="id">model</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs175', 679)" onmouseover="showTip(event, 'fs175', 679)" class="id">Counters</span><span class="pn">}</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs152', 680)" onmouseover="showTip(event, 'fs152', 680)" class="fn">view</span> <span onmouseout="hideTip(event, 'fs153', 681)" onmouseover="showTip(event, 'fs153', 681)" class="id">model</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs154', 682)" onmouseover="showTip(event, 'fs154', 682)" class="m">UI</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs74', 683)" onmouseover="showTip(event, 'fs74', 683)" class="id">button</span> <span class="s">"Add"</span> <span onmouseout="hideTip(event, 'fs178', 684)" onmouseover="showTip(event, 'fs178', 684)" class="uc">Insert</span> <span class="uc">::</span>
<span onmouseout="hideTip(event, 'fs154', 685)" onmouseover="showTip(event, 'fs154', 685)" class="m">UI</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs74', 686)" onmouseover="showTip(event, 'fs74', 686)" class="id">button</span> <span class="s">"Remove"</span> <span onmouseout="hideTip(event, 'fs179', 687)" onmouseover="showTip(event, 'fs179', 687)" class="uc">Remove</span> <span class="uc">::</span>
<span onmouseout="hideTip(event, 'fs82', 688)" onmouseover="showTip(event, 'fs82', 688)" class="m">List</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs181', 689)" onmouseover="showTip(event, 'fs181', 689)" class="id">mapi</span> <span class="pn">(</span><span class="k">fun</span> <span onmouseout="hideTip(event, 'fs18', 690)" onmouseover="showTip(event, 'fs18', 690)" class="id">i</span> <span onmouseout="hideTip(event, 'fs182', 691)" onmouseover="showTip(event, 'fs182', 691)" class="id">c</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs158', 692)" onmouseover="showTip(event, 'fs158', 692)" class="m">Counter</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs171', 693)" onmouseover="showTip(event, 'fs171', 693)" class="id">view</span> <span onmouseout="hideTip(event, 'fs182', 694)" onmouseover="showTip(event, 'fs182', 694)" class="id">c</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs154', 695)" onmouseover="showTip(event, 'fs154', 695)" class="m">UI</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs89', 696)" onmouseover="showTip(event, 'fs89', 696)" class="id">map</span> <span class="pn">(</span><span class="k">fun</span> <span onmouseout="hideTip(event, 'fs183', 697)" onmouseover="showTip(event, 'fs183', 697)" class="id">v</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs180', 698)" onmouseover="showTip(event, 'fs180', 698)" class="uc">Modify</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs18', 699)" onmouseover="showTip(event, 'fs18', 699)" class="id">i</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs183', 700)" onmouseover="showTip(event, 'fs183', 700)" class="id">v</span><span class="pn">)</span><span class="pn">)</span><span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs153', 701)" onmouseover="showTip(event, 'fs153', 701)" class="id">model</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs175', 702)" onmouseover="showTip(event, 'fs175', 702)" class="id">Counters</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs154', 703)" onmouseover="showTip(event, 'fs154', 703)" class="m">UI</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs79', 704)" onmouseover="showTip(event, 'fs79', 704)" class="id">div</span> <span onmouseout="hideTip(event, 'fs30', 705)" onmouseover="showTip(event, 'fs30', 705)" class="uc">Vertical</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs184', 706)" onmouseover="showTip(event, 'fs184', 706)" class="id">app</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs154', 707)" onmouseover="showTip(event, 'fs154', 707)" class="m">UI</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs126', 708)" onmouseover="showTip(event, 'fs126', 708)" class="id">app</span> <span onmouseout="hideTip(event, 'fs176', 709)" onmouseover="showTip(event, 'fs176', 709)" class="id">init</span> <span onmouseout="hideTip(event, 'fs168', 710)" onmouseover="showTip(event, 'fs168', 710)" class="fn">update</span> <span onmouseout="hideTip(event, 'fs152', 711)" onmouseover="showTip(event, 'fs152', 711)" class="fn">view</span>
</code></pre>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p><a href="http://guide.elm-lang.org/architecture/index.html">The Elm Architecture</a> pattern is very promising.
It produces type safe UIs that are highly composable.
Performance should be great even for large UIs while at the same time being able to target multiple UI frameworks.</p>
<p>UPDATED:</p>
<img style="border:1px solid black" src="/public/elm/donpraise.png"/>
<div class="tip" id="fs1">namespace System</div>
<div class="tip" id="fs2">namespace System.Runtime</div>
<div class="tip" id="fs3">namespace System.Runtime.CompilerServices</div>
<div class="tip" id="fs4">Multiple items<br />module List<br /><br />from Microsoft.FSharp.Collections<br /><br />--------------------<br />type List<'T> =<br />  | ( [] )<br />  | ( :: ) of Head: 'T * Tail: 'T list<br />    interface IReadOnlyList<'T><br />    interface IReadOnlyCollection<'T><br />    interface IEnumerable<br />    interface IEnumerable<'T><br />    member GetSlice : startIndex:int option * endIndex:int option -> 'T list<br />    member Head : 'T<br />    member IsEmpty : bool<br />    member Item : index:int -> 'T with get<br />    member Length : int<br />    member Tail : 'T list<br />    ...</div>
<div class="tip" id="fs5">val remove : n:int -> l:'a list -> 'a list * 'a list</div>
<div class="tip" id="fs6">val n : int</div>
<div class="tip" id="fs7">val l : 'a list</div>
<div class="tip" id="fs8">val pop : (int -> 'b list -> 'b list -> 'b list * 'b list)</div>
<div class="tip" id="fs9">val l : 'b list</div>
<div class="tip" id="fs10">val p : 'b list</div>
<div class="tip" id="fs11">val x : 'b</div>
<div class="tip" id="fs12">val xs : 'b list</div>
<div class="tip" id="fs13">val add : p:'a list -> l:'a list -> 'a list</div>
<div class="tip" id="fs14">val p : 'a list</div>
<div class="tip" id="fs15">val x : 'a</div>
<div class="tip" id="fs16">val xs : 'a list</div>
<div class="tip" id="fs17">val mapAt : i:int -> mapping:('a -> 'a) -> list:'a list -> 'a list</div>
<div class="tip" id="fs18">val i : int</div>
<div class="tip" id="fs19">val mapping : ('a -> 'a)</div>
<div class="tip" id="fs20">Multiple items<br />val list : 'a list<br /><br />--------------------<br />type 'T list = List<'T></div>
<div class="tip" id="fs21">val removed : 'a list</div>
<div class="tip" id="fs22">val tail : 'a list</div>
<div class="tip" id="fs23">val head : list:'T list -> 'T</div>
<div class="tip" id="fs24">val tail : list:'T list -> 'T list</div>
<div class="tip" id="fs25">Multiple items<br />module Event<br /><br />from Microsoft.FSharp.Control<br /><br />--------------------<br />type 'msg Event = ('msg -> unit) ref ref<br /><em><br /><br /> Message event used on the primitive UI components.</em><br /><br />--------------------<br />type Event<'Delegate,'Args (requires delegate and 'Delegate :> Delegate)> =<br />  new : unit -> Event<'Delegate,'Args><br />  member Trigger : sender:obj * args:'Args -> unit<br />  member Publish : IEvent<'Delegate,'Args><br /><br />--------------------<br />new : unit -> Event<'Delegate,'Args></div>
<div class="tip" id="fs26">type unit = Unit</div>
<div class="tip" id="fs27">Multiple items<br />val ref : value:'T -> 'T ref<br /><br />--------------------<br />type 'T ref = Ref<'T></div>
<div class="tip" id="fs28">type Layout =<br />  | Horizontal<br />  | Vertical<br /><em><br /><br /> Layout for a section of UI components.</em></div>
<div class="tip" id="fs29">union case Layout.Horizontal: Layout</div>
<div class="tip" id="fs30">union case Layout.Vertical: Layout</div>
<div class="tip" id="fs31">type UI =<br />  | Text of string<br />  | Input of string * string Event<br />  | Button of string * unit Event<br />  | Div of Layout * UI list<br /><em><br /><br /> Primitive UI components.</em></div>
<div class="tip" id="fs32">union case UI.Text: string -> UI</div>
<div class="tip" id="fs33">Multiple items<br />val string : value:'T -> string<br /><br />--------------------<br />type string = System.String</div>
<div class="tip" id="fs34">union case UI.Input: string * string Event -> UI</div>
<div class="tip" id="fs35">union case UI.Button: string * unit Event -> UI</div>
<div class="tip" id="fs36">union case UI.Div: Layout * UI list -> UI</div>
<div class="tip" id="fs37">type 'T list = List<'T></div>
<div class="tip" id="fs38">type UIUpdate =<br />  | InsertUI of int list * UI<br />  | UpdateUI of int list * UI<br />  | ReplaceUI of int list * UI<br />  | RemoveUI of int list<br />  | EventUI of (unit -> unit)<br /><em><br /><br /> UI component update and event redirection.</em></div>
<div class="tip" id="fs39">union case UIUpdate.InsertUI: int list * UI -> UIUpdate</div>
<div class="tip" id="fs40">Multiple items<br />val int : value:'T -> int (requires member op_Explicit)<br /><br />--------------------<br />type int = int32<br /><br />--------------------<br />type int<'Measure> = int</div>
<div class="tip" id="fs41">union case UIUpdate.UpdateUI: int list * UI -> UIUpdate</div>
<div class="tip" id="fs42">union case UIUpdate.ReplaceUI: int list * UI -> UIUpdate</div>
<div class="tip" id="fs43">union case UIUpdate.RemoveUI: int list -> UIUpdate</div>
<div class="tip" id="fs44">union case UIUpdate.EventUI: (unit -> unit) -> UIUpdate</div>
<div class="tip" id="fs45">Multiple items<br />type UI =<br />  | Text of string<br />  | Input of string * string Event<br />  | Button of string * unit Event<br />  | Div of Layout * UI list<br /><em><br /><br /> Primitive UI components.</em><br /><br />--------------------<br />type 'msg UI =<br />  {UI: UI;<br />   mutable Event: 'msg -> unit;}<br /><em><br /><br /> UI component including a message event.</em></div>
<div class="tip" id="fs46">Multiple items<br />UI.UI: UI<br /><br />--------------------<br />type UI =<br />  | Text of string<br />  | Input of string * string Event<br />  | Button of string * unit Event<br />  | Div of Layout * UI list<br /><em><br /><br /> Primitive UI components.</em><br /><br />--------------------<br />type 'msg UI =<br />  {UI: UI;<br />   mutable Event: 'msg -> unit;}<br /><em><br /><br /> UI component including a message event.</em></div>
<div class="tip" id="fs47">Multiple items<br />UI.Event: 'msg -> unit<br /><br />--------------------<br />module Event<br /><br />from Microsoft.FSharp.Control<br /><br />--------------------<br />type 'msg Event = ('msg -> unit) ref ref<br /><em><br /><br /> Message event used on the primitive UI components.</em><br /><br />--------------------<br />type Event<'Delegate,'Args (requires delegate and 'Delegate :> Delegate)> =<br />  new : unit -> Event<'Delegate,'Args><br />  member Trigger : sender:obj * args:'Args -> unit<br />  member Publish : IEvent<'Delegate,'Args><br /><br />--------------------<br />new : unit -> Event<'Delegate,'Args></div>
<div class="tip" id="fs48">type App<'msg,'model> =<br />  {Model: 'model;<br />   Update: 'msg -> 'model -> 'model;<br />   View: 'model -> 'msg UI;}<br /><em><br /><br /> UI application.</em></div>
<div class="tip" id="fs49">App.Model: 'model</div>
<div class="tip" id="fs50">App.Update: 'msg -> 'model -> 'model</div>
<div class="tip" id="fs51">App.View: 'model -> 'msg UI</div>
<div class="tip" id="fs52">type INativeUI =<br />  interface<br />    abstract member Send : UIUpdate list -> unit<br />  end<br /><em><br /><br /> Native UI interface.</em></div>
<div class="tip" id="fs53">Multiple items<br />type CompilationRepresentationAttribute =<br />  inherit Attribute<br />  new : flags:CompilationRepresentationFlags -> CompilationRepresentationAttribute<br />  member Flags : CompilationRepresentationFlags<br /><br />--------------------<br />new : flags:CompilationRepresentationFlags -> CompilationRepresentationAttribute</div>
<div class="tip" id="fs54">type CompilationRepresentationFlags =<br />  | None = 0<br />  | Static = 1<br />  | Instance = 2<br />  | ModuleSuffix = 4<br />  | UseNullAsTrueValue = 8<br />  | Event = 16</div>
<div class="tip" id="fs55">CompilationRepresentationFlags.ModuleSuffix: CompilationRepresentationFlags = 4</div>
<div class="tip" id="fs56">val memoize<'model,'msg (requires reference type and reference type)> : (('model -> 'msg UI) -> 'model -> 'msg UI) (requires reference type and reference type)<br /><em><br /><br /> Memoize view generation from model object references.</em></div>
<div class="tip" id="fs57">val not : value:bool -> bool</div>
<div class="tip" id="fs58">val d : ConditionalWeakTable<'model,'msg UI> (requires reference type and reference type)</div>
<div class="tip" id="fs59">Multiple items<br />type ConditionalWeakTable<'TKey,'TValue (requires reference type and reference type)> =<br />  new : unit -> ConditionalWeakTable<'TKey, 'TValue><br />  member Add : key:'TKey * value:'TValue -> unit<br />  member GetOrCreateValue : key:'TKey -> 'TValue<br />  member GetValue : key:'TKey * createValueCallback:CreateValueCallback<'TKey, 'TValue> -> 'TValue<br />  member Remove : key:'TKey -> bool<br />  member TryGetValue : key:'TKey * value:'TValue -> bool<br />  nested type CreateValueCallback<br /><br />--------------------<br />ConditionalWeakTable() : ConditionalWeakTable<'TKey,'TValue></div>
<div class="tip" id="fs60">val view : ('model -> 'msg UI) (requires reference type and reference type)</div>
<div class="tip" id="fs61">val model : 'model (requires reference type)</div>
<div class="tip" id="fs62">ConditionalWeakTable.TryGetValue(key: 'model, value: byref<'msg UI>) : bool</div>
<div class="tip" id="fs63">val ui : 'msg UI (requires reference type)</div>
<div class="tip" id="fs64">ConditionalWeakTable.Add(key: 'model, value: 'msg UI) : unit</div>
<div class="tip" id="fs65">val text : text:string -> 'a UI<br /><em><br /><br /> Returns a Text display UI component.</em></div>
<div class="tip" id="fs66">val text : string</div>
<div class="tip" id="fs67">val ignore : value:'T -> unit</div>
<div class="tip" id="fs68">val input : text:string -> string UI<br /><em><br /><br /> Returns a text Input UI component.</em></div>
<div class="tip" id="fs69">val ev : (string -> unit) ref ref</div>
<div class="tip" id="fs70">val ui : string UI</div>
<div class="tip" id="fs71">val raise : (string -> unit)</div>
<div class="tip" id="fs72">val a : string</div>
<div class="tip" id="fs73">UI.Event: string -> unit</div>
<div class="tip" id="fs74">val button : text:string -> msg:'a -> 'a UI<br /><em><br /><br /> Returns a Button UI component.</em></div>
<div class="tip" id="fs75">val msg : 'a</div>
<div class="tip" id="fs76">val ev : (unit -> unit) ref ref</div>
<div class="tip" id="fs77">val ui : 'a UI</div>
<div class="tip" id="fs78">UI.Event: 'a -> unit</div>
<div class="tip" id="fs79">val div : layout:Layout -> list:'a UI list -> 'a UI<br /><em><br /><br /> Returns a section of UI components given a layout.<br /> The name div comes from HTML and represents a division (or section) of the UI.</em></div>
<div class="tip" id="fs80">val layout : Layout</div>
<div class="tip" id="fs81">Multiple items<br />val list : 'a UI list<br /><br />--------------------<br />type 'T list = List<'T></div>
<div class="tip" id="fs82">Multiple items<br />module List<br /><br />from Main<br /><br />--------------------<br />module List<br /><br />from Microsoft.FSharp.Collections<br /><br />--------------------<br />type List<'T> =<br />  | ( [] )<br />  | ( :: ) of Head: 'T * Tail: 'T list<br />    interface IReadOnlyList<'T><br />    interface IReadOnlyCollection<'T><br />    interface IEnumerable<br />    interface IEnumerable<'T><br />    member GetSlice : startIndex:int option * endIndex:int option -> 'T list<br />    member Head : 'T<br />    member IsEmpty : bool<br />    member Item : index:int -> 'T with get<br />    member Length : int<br />    member Tail : 'T list<br />    ...</div>
<div class="tip" id="fs83">val map : mapping:('T -> 'U) -> list:'T list -> 'U list</div>
<div class="tip" id="fs84">UI.UI: UI</div>
<div class="tip" id="fs85">val raise : ('a -> unit)</div>
<div class="tip" id="fs86">val a : 'a</div>
<div class="tip" id="fs87">val iter : action:('T -> unit) -> list:'T list -> unit</div>
<div class="tip" id="fs88">val i : 'a UI</div>
<div class="tip" id="fs89">val map : f:('a -> 'b) -> ui:'a UI -> 'b UI<br /><em><br /><br /> Returns a new UI component mapping the message event using the given function.</em></div>
<div class="tip" id="fs90">val f : ('a -> 'b)</div>
<div class="tip" id="fs91">val ui2 : 'b UI</div>
<div class="tip" id="fs92">val e : 'a</div>
<div class="tip" id="fs93">UI.Event: 'b -> unit</div>
<div class="tip" id="fs94">val diff : ui1:'a UI -> ui2:'b UI -> UIUpdate list<br /><em><br /><br /> Returns a list of UI updates from two UI components.</em></div>
<div class="tip" id="fs95">val ui1 : 'a UI</div>
<div class="tip" id="fs96">val update : ('c ref ref -> 'c ref ref -> unit -> unit)</div>
<div class="tip" id="fs97">val e1 : 'c ref ref</div>
<div class="tip" id="fs98">val e2 : 'c ref ref</div>
<div class="tip" id="fs99">val ev : 'c ref</div>
<div class="tip" id="fs100">val diff : (UI -> UI -> int list -> int -> UIUpdate list -> UIUpdate list)</div>
<div class="tip" id="fs101">val ui1 : UI</div>
<div class="tip" id="fs102">val ui2 : UI</div>
<div class="tip" id="fs103">val path : int list</div>
<div class="tip" id="fs104">val index : int</div>
<div class="tip" id="fs105">val diffs : UIUpdate list</div>
<div class="tip" id="fs106">module LanguagePrimitives<br /><br />from Microsoft.FSharp.Core</div>
<div class="tip" id="fs107">val PhysicalEquality : e1:'T -> e2:'T -> bool (requires reference type)</div>
<div class="tip" id="fs108">val t1 : string</div>
<div class="tip" id="fs109">val t2 : string</div>
<div class="tip" id="fs110">val e1 : unit Event</div>
<div class="tip" id="fs111">val e2 : unit Event</div>
<div class="tip" id="fs112">val e1 : string Event</div>
<div class="tip" id="fs113">val e2 : string Event</div>
<div class="tip" id="fs114">val l1 : Layout</div>
<div class="tip" id="fs115">val l2 : Layout</div>
<div class="tip" id="fs116">val l : UI list</div>
<div class="tip" id="fs117">val fold : folder:('State -> 'T -> 'State) -> state:'State -> list:'T list -> 'State</div>
<div class="tip" id="fs118">val ui : UI</div>
<div class="tip" id="fs119">val snd : tuple:('T1 * 'T2) -> 'T2</div>
<div class="tip" id="fs120">val l : Layout</div>
<div class="tip" id="fs121">val h1 : UI</div>
<div class="tip" id="fs122">val t1 : UI list</div>
<div class="tip" id="fs123">val h2 : UI</div>
<div class="tip" id="fs124">val t2 : UI list</div>
<div class="tip" id="fs125">val h3 : UI</div>
<div class="tip" id="fs126">val app : model:'a -> update:('b -> 'a -> 'a) -> view:('a -> 'b UI) -> App<'b,'a><br /><em><br /><br /> Returns a UI application from a UI model, update and view.</em></div>
<div class="tip" id="fs127">val model : 'a</div>
<div class="tip" id="fs128">val update : ('b -> 'a -> 'a)</div>
<div class="tip" id="fs129">val view : ('a -> 'b UI)</div>
<div class="tip" id="fs130">val run : nativeUI:INativeUI -> app:App<'a,'b> -> unit<br /><em><br /><br /> Runs a UI application given a native UI.</em></div>
<div class="tip" id="fs131">val nativeUI : INativeUI</div>
<div class="tip" id="fs132">val app : App<'a,'b></div>
<div class="tip" id="fs133">val handle : ('b -> 'a UI -> 'a -> unit)</div>
<div class="tip" id="fs134">val model : 'b</div>
<div class="tip" id="fs135">val newModel : 'b</div>
<div class="tip" id="fs136">App.Update: 'a -> 'b -> 'b</div>
<div class="tip" id="fs137">val newUI : 'a UI</div>
<div class="tip" id="fs138">App.View: 'b -> 'a UI</div>
<div class="tip" id="fs139">val diff : UIUpdate list</div>
<div class="tip" id="fs140">val f : (unit -> unit)</div>
<div class="tip" id="fs141">abstract member INativeUI.Send : UIUpdate list -> unit</div>
<div class="tip" id="fs142">App.Model: 'b</div>
<div class="tip" id="fs143">type Model = int</div>
<div class="tip" id="fs144">val init : i:Model -> Model</div>
<div class="tip" id="fs145">val i : Model</div>
<div class="tip" id="fs146">type Msg =<br />  | Increment<br />  | Decrement</div>
<div class="tip" id="fs147">union case Msg.Increment: Msg</div>
<div class="tip" id="fs148">union case Msg.Decrement: Msg</div>
<div class="tip" id="fs149">val update : msg:Msg -> model:int -> int</div>
<div class="tip" id="fs150">val msg : Msg</div>
<div class="tip" id="fs151">val model : int</div>
<div class="tip" id="fs152">val view : model:Model -> Msg UI</div>
<div class="tip" id="fs153">val model : Model</div>
<div class="tip" id="fs154">Multiple items<br />module UI<br /><br />from Main<br /><br />--------------------<br />type UI =<br />  | Text of string<br />  | Input of string * string Event<br />  | Button of string * unit Event<br />  | Div of Layout * UI list<br /><em><br /><br /> Primitive UI components.</em><br /><br />--------------------<br />type 'msg UI =<br />  {UI: UI;<br />   mutable Event: 'msg -> unit;}<br /><em><br /><br /> UI component including a message event.</em></div>
<div class="tip" id="fs155">val app : i:Model -> App<Msg,Model></div>
<div class="tip" id="fs156">type Model =<br />  {Top: Model;<br />   Bottom: Model;}</div>
<div class="tip" id="fs157">Model.Top: Counter.Model</div>
<div class="tip" id="fs158">module Counter<br /><br />from Main</div>
<div class="tip" id="fs159">Model.Bottom: Counter.Model</div>
<div class="tip" id="fs160">val init : top:Counter.Model -> bottom:Counter.Model -> Model</div>
<div class="tip" id="fs161">val top : Counter.Model</div>
<div class="tip" id="fs162">val bottom : Counter.Model</div>
<div class="tip" id="fs163">val init : i:Counter.Model -> Counter.Model</div>
<div class="tip" id="fs164">type Msg =<br />  | Reset<br />  | Top of Msg<br />  | Bottom of Msg</div>
<div class="tip" id="fs165">union case Msg.Reset: Msg</div>
<div class="tip" id="fs166">union case Msg.Top: Counter.Msg -> Msg</div>
<div class="tip" id="fs167">union case Msg.Bottom: Counter.Msg -> Msg</div>
<div class="tip" id="fs168">val update : msg:Msg -> model:Model -> Model</div>
<div class="tip" id="fs169">val msg : Counter.Msg</div>
<div class="tip" id="fs170">val update : msg:Counter.Msg -> model:int -> int</div>
<div class="tip" id="fs171">val view : model:Counter.Model -> Counter.Msg UI</div>
<div class="tip" id="fs172">val app : top:Counter.Model -> bottom:Counter.Model -> App<Msg,Model></div>
<div class="tip" id="fs173">module CounterList<br /><br />from Main</div>
<div class="tip" id="fs174">type Model =<br />  {Counters: Model list;}</div>
<div class="tip" id="fs175">Model.Counters: Counter.Model list</div>
<div class="tip" id="fs176">val init : Model</div>
<div class="tip" id="fs177">type Msg =<br />  | Insert<br />  | Remove<br />  | Modify of int * Msg</div>
<div class="tip" id="fs178">union case Msg.Insert: Msg</div>
<div class="tip" id="fs179">union case Msg.Remove: Msg</div>
<div class="tip" id="fs180">union case Msg.Modify: int * Counter.Msg -> Msg</div>
<div class="tip" id="fs181">val mapi : mapping:(int -> 'T -> 'U) -> list:'T list -> 'U list</div>
<div class="tip" id="fs182">val c : Counter.Model</div>
<div class="tip" id="fs183">val v : Counter.Msg</div>
<div class="tip" id="fs184">val app : App<Msg,Model></div>
Modularity from Lazy Evaluation - Performance Testing
2016-05-20T00:00:00+01:00
http://anthonylloyd.github.io/blog/2016/05/20/performance-testing
<p>This is the second example of how higher-order functions and lazy evaluation can reduce complexity and lead to more modular software.</p>
<h2><a name="Background" class="anchor" href="#Background">Background</a></h2>
<p>To performance test code a number of iterations have to be performed so a more stable average can be compared.
The number of iterations is usually an input and chosen arbitrarily.</p>
<p>This post covers how statistical tests can be used to remove the need for this input.
This simplifies performance testing and makes the results more robust and useful.</p>
<h2><a name="Statistics" class="anchor" href="#Statistics">Statistics</a></h2>
<p>Lazy evaluation can be used to produce an <a href="https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Online_algorithm">online</a> sample statistics sequence.</p>
<p>The <a href="https://en.wikipedia.org/wiki/Standard_error">standard error</a> of a sample mean is given by</p>
<p><span class="math">\[SE_{\bar{x}} = \frac{s}{\sqrt{n}}\]</span></p>
<p>The statistics sequence can be iterated until a given mean standard error level of accuracy is achieved.</p>
<p><a href="https://en.wikipedia.org/wiki/Welch%27s_t-test">Welch's t-test</a> for comparing the mean of two samples is given by</p>
<p><span class="math">\[t = \frac{\bar{x}_1-\bar{x}_2}{\sqrt{f_1+f_2}}\]</span></p>
<p><span class="math">\[df = \frac{\left(f_1+f_2\right)^2}{\frac{f_1^2}{n_1-1}+\frac{f_2^2}{n_2-1}}\]</span></p>
<p><span class="math">\[f_i = \frac{s_i^2}{n_i}\]</span></p>
<p>where <span class="math">\(n\)</span> is the sample size, <span class="math">\(\bar{x}\)</span> is the sample mean, <span class="math">\(s^2\)</span> is the sample variance, <span class="math">\(t\)</span> is Welch's t statistic, and <span class="math">\(df\)</span> is the degrees of freedom of the statistic.</p>
<p>Welch's t statistic can be compared to the inverse Student's t-distribution for a given confidence level to test if the sample means are different.</p>
<p>Two sample statistics sequences can be iterated together and compared to decide if one mean is larger than the other.</p>
<h2><a name="Statistics-code" class="anchor" href="#Statistics-code">Statistics code</a></h2>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="k">type</span> <span class="rt">SampleStatistics</span> <span class="o">=</span> <span class="pn">{</span><span onmouseout="hideTip(event, 'fs6', 9)" onmouseover="showTip(event, 'fs6', 9)" class="id">N</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs7', 10)" onmouseover="showTip(event, 'fs7', 10)" class="vt">int</span><span class="pn">;</span><span onmouseout="hideTip(event, 'fs8', 11)" onmouseover="showTip(event, 'fs8', 11)" class="id">Mean</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs9', 12)" onmouseover="showTip(event, 'fs9', 12)" class="vt">float</span><span class="pn">;</span><span onmouseout="hideTip(event, 'fs10', 13)" onmouseover="showTip(event, 'fs10', 13)" class="id">Variance</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs9', 14)" onmouseover="showTip(event, 'fs9', 14)" class="vt">float</span><span class="pn">}</span>
<span class="k">member</span> <span onmouseout="hideTip(event, 'fs11', 15)" onmouseover="showTip(event, 'fs11', 15)" class="id">s</span><span class="pn">.</span><span class="prop">StandardDeviation</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs12', 16)" onmouseover="showTip(event, 'fs12', 16)" class="fn">sqrt</span> <span onmouseout="hideTip(event, 'fs11', 17)" onmouseover="showTip(event, 'fs11', 17)" class="id">s</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs10', 18)" onmouseover="showTip(event, 'fs10', 18)" class="id">Variance</span>
<span class="k">member</span> <span onmouseout="hideTip(event, 'fs11', 19)" onmouseover="showTip(event, 'fs11', 19)" class="id">s</span><span class="pn">.</span><span class="prop">MeanStandardError</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs12', 20)" onmouseover="showTip(event, 'fs12', 20)" class="fn">sqrt</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs11', 21)" onmouseover="showTip(event, 'fs11', 21)" class="id">s</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs10', 22)" onmouseover="showTip(event, 'fs10', 22)" class="id">Variance</span><span class="o">/</span><span onmouseout="hideTip(event, 'fs9', 23)" onmouseover="showTip(event, 'fs9', 23)" class="fn">float</span> <span onmouseout="hideTip(event, 'fs11', 24)" onmouseover="showTip(event, 'fs11', 24)" class="id">s</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs6', 25)" onmouseover="showTip(event, 'fs6', 25)" class="id">N</span><span class="pn">)</span>
<span class="k">type</span> <span onmouseout="hideTip(event, 'fs13', 26)" onmouseover="showTip(event, 'fs13', 26)" class="rt">WelchStatistic</span> <span class="o">=</span> <span class="pn">{</span><span onmouseout="hideTip(event, 'fs14', 27)" onmouseover="showTip(event, 'fs14', 27)" class="id">T</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs9', 28)" onmouseover="showTip(event, 'fs9', 28)" class="vt">float</span><span class="pn">;</span><span onmouseout="hideTip(event, 'fs15', 29)" onmouseover="showTip(event, 'fs15', 29)" class="id">DF</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs7', 30)" onmouseover="showTip(event, 'fs7', 30)" class="vt">int</span><span class="pn">}</span>
<span class="k">module</span> <span class="m">Statistics</span> <span class="o">=</span>
<span class="c">/// Online statistics sequence for a given sample sequence.</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs16', 31)" onmouseover="showTip(event, 'fs16', 31)" class="fn">sampleStatistics</span> <span onmouseout="hideTip(event, 'fs17', 32)" onmouseover="showTip(event, 'fs17', 32)" class="id">s</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs18', 33)" onmouseover="showTip(event, 'fs18', 33)" class="fn">calc</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs19', 34)" onmouseover="showTip(event, 'fs19', 34)" class="id">n</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs20', 35)" onmouseover="showTip(event, 'fs20', 35)" class="id">m</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs21', 36)" onmouseover="showTip(event, 'fs21', 36)" class="id">s</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs22', 37)" onmouseover="showTip(event, 'fs22', 37)" class="id">x</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs23', 38)" onmouseover="showTip(event, 'fs23', 38)" class="id">m'</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs20', 39)" onmouseover="showTip(event, 'fs20', 39)" class="id">m</span><span class="o">+</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs22', 40)" onmouseover="showTip(event, 'fs22', 40)" class="id">x</span><span class="o">-</span><span onmouseout="hideTip(event, 'fs20', 41)" onmouseover="showTip(event, 'fs20', 41)" class="id">m</span><span class="pn">)</span><span class="o">/</span><span onmouseout="hideTip(event, 'fs9', 42)" onmouseover="showTip(event, 'fs9', 42)" class="fn">float</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs19', 43)" onmouseover="showTip(event, 'fs19', 43)" class="id">n</span><span class="o">+</span><span class="n">1</span><span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs19', 44)" onmouseover="showTip(event, 'fs19', 44)" class="id">n</span><span class="o">+</span><span class="n">1</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs23', 45)" onmouseover="showTip(event, 'fs23', 45)" class="id">m'</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs21', 46)" onmouseover="showTip(event, 'fs21', 46)" class="id">s</span><span class="o">+</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs22', 47)" onmouseover="showTip(event, 'fs22', 47)" class="id">x</span><span class="o">-</span><span onmouseout="hideTip(event, 'fs20', 48)" onmouseover="showTip(event, 'fs20', 48)" class="id">m</span><span class="pn">)</span><span class="o">*</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs22', 49)" onmouseover="showTip(event, 'fs22', 49)" class="id">x</span><span class="o">-</span><span onmouseout="hideTip(event, 'fs23', 50)" onmouseover="showTip(event, 'fs23', 50)" class="id">m'</span><span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs24', 51)" onmouseover="showTip(event, 'fs24', 51)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs25', 52)" onmouseover="showTip(event, 'fs25', 52)" class="id">map</span> <span onmouseout="hideTip(event, 'fs9', 53)" onmouseover="showTip(event, 'fs9', 53)" class="fn">float</span> <span onmouseout="hideTip(event, 'fs17', 54)" onmouseover="showTip(event, 'fs17', 54)" class="id">s</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs24', 55)" onmouseover="showTip(event, 'fs24', 55)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs26', 56)" onmouseover="showTip(event, 'fs26', 56)" class="id">scan</span> <span onmouseout="hideTip(event, 'fs18', 57)" onmouseover="showTip(event, 'fs18', 57)" class="fn">calc</span> <span class="pn">(</span><span class="n">0</span><span class="pn">,</span><span class="n">0.0</span><span class="pn">,</span><span class="n">0.0</span><span class="pn">)</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs24', 58)" onmouseover="showTip(event, 'fs24', 58)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs27', 59)" onmouseover="showTip(event, 'fs27', 59)" class="id">skip</span> <span class="n">3</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs24', 60)" onmouseover="showTip(event, 'fs24', 60)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs25', 61)" onmouseover="showTip(event, 'fs25', 61)" class="id">map</span> <span class="pn">(</span><span class="k">fun</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs19', 62)" onmouseover="showTip(event, 'fs19', 62)" class="id">n</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs20', 63)" onmouseover="showTip(event, 'fs20', 63)" class="id">m</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs21', 64)" onmouseover="showTip(event, 'fs21', 64)" class="id">s</span><span class="pn">)</span> <span class="k">-></span> <span class="pn">{</span><span onmouseout="hideTip(event, 'fs6', 65)" onmouseover="showTip(event, 'fs6', 65)" class="id">N</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs19', 66)" onmouseover="showTip(event, 'fs19', 66)" class="id">n</span><span class="pn">;</span><span class="id">Mean</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs20', 67)" onmouseover="showTip(event, 'fs20', 67)" class="id">m</span><span class="pn">;</span><span class="id">Variance</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs21', 68)" onmouseover="showTip(event, 'fs21', 68)" class="id">s</span><span class="o">/</span><span onmouseout="hideTip(event, 'fs9', 69)" onmouseover="showTip(event, 'fs9', 69)" class="fn">float</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs19', 70)" onmouseover="showTip(event, 'fs19', 70)" class="id">n</span><span class="o">-</span><span class="n">1</span><span class="pn">)</span><span class="pn">}</span><span class="pn">)</span>
<span class="c">/// Scale the statistics for a given underlying random variable change of scale.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs28', 71)" onmouseover="showTip(event, 'fs28', 71)" class="fn">scale</span> <span onmouseout="hideTip(event, 'fs29', 72)" onmouseover="showTip(event, 'fs29', 72)" class="id">f</span> <span onmouseout="hideTip(event, 'fs11', 73)" onmouseover="showTip(event, 'fs11', 73)" class="id">s</span> <span class="o">=</span> <span class="pn">{</span><span onmouseout="hideTip(event, 'fs11', 74)" onmouseover="showTip(event, 'fs11', 74)" class="id">s</span> <span class="k">with</span> <span class="id">Mean</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs11', 75)" onmouseover="showTip(event, 'fs11', 75)" class="id">s</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs8', 76)" onmouseover="showTip(event, 'fs8', 76)" class="id">Mean</span><span class="o">*</span><span onmouseout="hideTip(event, 'fs29', 77)" onmouseover="showTip(event, 'fs29', 77)" class="id">f</span><span class="pn">;</span><span class="id">Variance</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs11', 78)" onmouseover="showTip(event, 'fs11', 78)" class="id">s</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs10', 79)" onmouseover="showTip(event, 'fs10', 79)" class="id">Variance</span><span class="o">*</span><span onmouseout="hideTip(event, 'fs4', 80)" onmouseover="showTip(event, 'fs4', 80)" class="fn">sqr</span> <span onmouseout="hideTip(event, 'fs29', 81)" onmouseover="showTip(event, 'fs29', 81)" class="id">f</span><span class="pn">}</span>
<span class="c">/// Single iteration statistics for a given iteration count and total statistics.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs30', 82)" onmouseover="showTip(event, 'fs30', 82)" class="fn">singleIteration</span> <span onmouseout="hideTip(event, 'fs31', 83)" onmouseover="showTip(event, 'fs31', 83)" class="id">ic</span> <span onmouseout="hideTip(event, 'fs11', 84)" onmouseover="showTip(event, 'fs11', 84)" class="id">s</span> <span class="o">=</span>
<span class="pn">{</span><span onmouseout="hideTip(event, 'fs6', 85)" onmouseover="showTip(event, 'fs6', 85)" class="id">N</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs11', 86)" onmouseover="showTip(event, 'fs11', 86)" class="id">s</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs6', 87)" onmouseover="showTip(event, 'fs6', 87)" class="id">N</span><span class="o">*</span><span onmouseout="hideTip(event, 'fs31', 88)" onmouseover="showTip(event, 'fs31', 88)" class="id">ic</span><span class="pn">;</span><span class="id">Mean</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs11', 89)" onmouseover="showTip(event, 'fs11', 89)" class="id">s</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs8', 90)" onmouseover="showTip(event, 'fs8', 90)" class="id">Mean</span><span class="o">/</span><span onmouseout="hideTip(event, 'fs9', 91)" onmouseover="showTip(event, 'fs9', 91)" class="fn">float</span> <span onmouseout="hideTip(event, 'fs31', 92)" onmouseover="showTip(event, 'fs31', 92)" class="id">ic</span><span class="pn">;</span><span class="id">Variance</span><span class="o">=</span><span onmouseout="hideTip(event, 'fs11', 93)" onmouseover="showTip(event, 'fs11', 93)" class="id">s</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs10', 94)" onmouseover="showTip(event, 'fs10', 94)" class="id">Variance</span><span class="o">/</span><span onmouseout="hideTip(event, 'fs9', 95)" onmouseover="showTip(event, 'fs9', 95)" class="fn">float</span> <span onmouseout="hideTip(event, 'fs31', 96)" onmouseover="showTip(event, 'fs31', 96)" class="id">ic</span><span class="pn">}</span>
<span class="c">/// Student's t-distribution inverse for 0.1% confidence level by</span>
<span class="c">/// degrees of freedom.</span>
<span class="k">let</span> <span class="k">private</span> <span onmouseout="hideTip(event, 'fs32', 97)" onmouseover="showTip(event, 'fs32', 97)" class="id">tInv</span> <span class="o">=</span>
<span class="pn">[|</span><span class="n">636.6</span><span class="pn">;</span><span class="n">31.60</span><span class="pn">;</span><span class="n">12.92</span><span class="pn">;</span><span class="n">8.610</span><span class="pn">;</span><span class="n">6.869</span><span class="pn">;</span><span class="n">5.959</span><span class="pn">;</span><span class="n">5.408</span><span class="pn">;</span><span class="n">5.041</span><span class="pn">;</span><span class="n">4.781</span><span class="pn">;</span><span class="n">4.587</span><span class="pn">;</span><span class="n">4.437</span><span class="pn">;</span><span class="n">4.318</span><span class="pn">;</span><span class="n">4.221</span><span class="pn">;</span>
<span class="n">4.140</span><span class="pn">;</span><span class="n">4.073</span><span class="pn">;</span><span class="n">4.015</span><span class="pn">;</span><span class="n">3.965</span><span class="pn">;</span><span class="n">3.922</span><span class="pn">;</span><span class="n">3.883</span><span class="pn">;</span><span class="n">3.850</span><span class="pn">;</span><span class="n">3.819</span><span class="pn">;</span><span class="n">3.792</span><span class="pn">;</span><span class="n">3.768</span><span class="pn">;</span><span class="n">3.745</span><span class="pn">;</span><span class="n">3.725</span><span class="pn">;</span><span class="n">3.707</span><span class="pn">;</span>
<span class="n">3.690</span><span class="pn">;</span><span class="n">3.674</span><span class="pn">;</span><span class="n">3.659</span><span class="pn">;</span><span class="n">3.646</span><span class="pn">;</span><span class="n">3.633</span><span class="pn">;</span><span class="n">3.622</span><span class="pn">;</span><span class="n">3.611</span><span class="pn">;</span><span class="n">3.601</span><span class="pn">;</span><span class="n">3.591</span><span class="pn">;</span><span class="n">3.582</span><span class="pn">;</span><span class="n">3.574</span><span class="pn">;</span><span class="n">3.566</span><span class="pn">;</span><span class="n">3.558</span><span class="pn">;</span>
<span class="n">3.551</span><span class="pn">;</span><span class="n">3.544</span><span class="pn">;</span><span class="n">3.538</span><span class="pn">;</span><span class="n">3.532</span><span class="pn">;</span><span class="n">3.526</span><span class="pn">;</span><span class="n">3.520</span><span class="pn">;</span><span class="n">3.515</span><span class="pn">;</span><span class="n">3.510</span><span class="pn">;</span><span class="n">3.505</span><span class="pn">;</span><span class="n">3.500</span><span class="pn">;</span><span class="n">3.496</span><span class="pn">;</span><span class="n">3.492</span><span class="pn">;</span><span class="n">3.488</span><span class="pn">;</span>
<span class="n">3.484</span><span class="pn">;</span><span class="n">3.480</span><span class="pn">;</span><span class="n">3.476</span><span class="pn">;</span><span class="n">3.473</span><span class="pn">;</span><span class="n">3.470</span><span class="pn">;</span><span class="n">3.466</span><span class="pn">;</span><span class="n">3.463</span><span class="pn">;</span><span class="n">3.460</span><span class="pn">;</span><span class="n">3.457</span><span class="pn">;</span><span class="n">3.454</span><span class="pn">;</span><span class="n">3.452</span><span class="pn">;</span><span class="n">3.449</span><span class="pn">;</span><span class="n">3.447</span><span class="pn">;</span>
<span class="n">3.444</span><span class="pn">;</span><span class="n">3.442</span><span class="pn">;</span><span class="n">3.439</span><span class="pn">;</span><span class="n">3.437</span><span class="pn">;</span><span class="n">3.435</span><span class="pn">;</span><span class="n">3.433</span><span class="pn">;</span><span class="n">3.431</span><span class="pn">;</span><span class="n">3.429</span><span class="pn">;</span><span class="n">3.427</span><span class="pn">;</span><span class="n">3.425</span><span class="pn">;</span><span class="n">3.423</span><span class="pn">;</span><span class="n">3.421</span><span class="pn">;</span><span class="n">3.420</span><span class="pn">;</span>
<span class="n">3.418</span><span class="pn">;</span><span class="n">3.416</span><span class="pn">;</span><span class="n">3.415</span><span class="pn">;</span><span class="n">3.413</span><span class="pn">;</span><span class="n">3.412</span><span class="pn">;</span><span class="n">3.410</span><span class="pn">;</span><span class="n">3.409</span><span class="pn">;</span><span class="n">3.407</span><span class="pn">;</span><span class="n">3.406</span><span class="pn">;</span><span class="n">3.405</span><span class="pn">;</span><span class="n">3.403</span><span class="pn">;</span><span class="n">3.402</span><span class="pn">;</span><span class="n">3.401</span><span class="pn">;</span>
<span class="n">3.399</span><span class="pn">;</span><span class="n">3.398</span><span class="pn">;</span><span class="n">3.397</span><span class="pn">;</span><span class="n">3.396</span><span class="pn">;</span><span class="n">3.395</span><span class="pn">;</span><span class="n">3.394</span><span class="pn">;</span><span class="n">3.393</span><span class="pn">;</span><span class="n">3.392</span><span class="pn">;</span><span class="n">3.390</span><span class="pn">|]</span>
<span class="c">/// Welch's t-test statistic for two given sample statistics.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs33', 98)" onmouseover="showTip(event, 'fs33', 98)" class="fn">welchStatistic</span> <span onmouseout="hideTip(event, 'fs34', 99)" onmouseover="showTip(event, 'fs34', 99)" class="id">s1</span> <span onmouseout="hideTip(event, 'fs35', 100)" onmouseover="showTip(event, 'fs35', 100)" class="id">s2</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs36', 101)" onmouseover="showTip(event, 'fs36', 101)" class="id">f1</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs34', 102)" onmouseover="showTip(event, 'fs34', 102)" class="id">s1</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs10', 103)" onmouseover="showTip(event, 'fs10', 103)" class="id">Variance</span><span class="o">/</span><span onmouseout="hideTip(event, 'fs9', 104)" onmouseover="showTip(event, 'fs9', 104)" class="fn">float</span> <span onmouseout="hideTip(event, 'fs34', 105)" onmouseover="showTip(event, 'fs34', 105)" class="id">s1</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs6', 106)" onmouseover="showTip(event, 'fs6', 106)" class="id">N</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs37', 107)" onmouseover="showTip(event, 'fs37', 107)" class="id">f2</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs35', 108)" onmouseover="showTip(event, 'fs35', 108)" class="id">s2</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs10', 109)" onmouseover="showTip(event, 'fs10', 109)" class="id">Variance</span><span class="o">/</span><span onmouseout="hideTip(event, 'fs9', 110)" onmouseover="showTip(event, 'fs9', 110)" class="fn">float</span> <span onmouseout="hideTip(event, 'fs35', 111)" onmouseover="showTip(event, 'fs35', 111)" class="id">s2</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs6', 112)" onmouseover="showTip(event, 'fs6', 112)" class="id">N</span>
<span class="pn">{</span>
<span onmouseout="hideTip(event, 'fs14', 113)" onmouseover="showTip(event, 'fs14', 113)" class="id">T</span> <span class="o">=</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs34', 114)" onmouseover="showTip(event, 'fs34', 114)" class="id">s1</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs8', 115)" onmouseover="showTip(event, 'fs8', 115)" class="id">Mean</span><span class="o">-</span><span onmouseout="hideTip(event, 'fs35', 116)" onmouseover="showTip(event, 'fs35', 116)" class="id">s2</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs8', 117)" onmouseover="showTip(event, 'fs8', 117)" class="id">Mean</span><span class="pn">)</span><span class="o">/</span><span onmouseout="hideTip(event, 'fs12', 118)" onmouseover="showTip(event, 'fs12', 118)" class="fn">sqrt</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs36', 119)" onmouseover="showTip(event, 'fs36', 119)" class="id">f1</span><span class="o">+</span><span onmouseout="hideTip(event, 'fs37', 120)" onmouseover="showTip(event, 'fs37', 120)" class="id">f2</span><span class="pn">)</span>
<span class="id">DF</span><span class="o">=</span> <span class="k">if</span> <span onmouseout="hideTip(event, 'fs36', 121)" onmouseover="showTip(event, 'fs36', 121)" class="id">f1</span><span class="o">=</span><span class="n">0.0</span> <span class="o">&&</span> <span onmouseout="hideTip(event, 'fs37', 122)" onmouseover="showTip(event, 'fs37', 122)" class="id">f2</span><span class="o">=</span><span class="n">0.0</span> <span class="k">then</span> <span class="n">1</span>
<span class="k">else</span> <span onmouseout="hideTip(event, 'fs4', 123)" onmouseover="showTip(event, 'fs4', 123)" class="fn">sqr</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs36', 124)" onmouseover="showTip(event, 'fs36', 124)" class="id">f1</span><span class="o">+</span><span onmouseout="hideTip(event, 'fs37', 125)" onmouseover="showTip(event, 'fs37', 125)" class="id">f2</span><span class="pn">)</span><span class="o">/</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs4', 126)" onmouseover="showTip(event, 'fs4', 126)" class="fn">sqr</span> <span onmouseout="hideTip(event, 'fs36', 127)" onmouseover="showTip(event, 'fs36', 127)" class="id">f1</span><span class="o">/</span><span onmouseout="hideTip(event, 'fs9', 128)" onmouseover="showTip(event, 'fs9', 128)" class="fn">float</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs34', 129)" onmouseover="showTip(event, 'fs34', 129)" class="id">s1</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs6', 130)" onmouseover="showTip(event, 'fs6', 130)" class="id">N</span><span class="o">-</span><span class="n">1</span><span class="pn">)</span><span class="o">+</span><span onmouseout="hideTip(event, 'fs4', 131)" onmouseover="showTip(event, 'fs4', 131)" class="fn">sqr</span> <span onmouseout="hideTip(event, 'fs37', 132)" onmouseover="showTip(event, 'fs37', 132)" class="id">f2</span><span class="o">/</span><span onmouseout="hideTip(event, 'fs9', 133)" onmouseover="showTip(event, 'fs9', 133)" class="fn">float</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs35', 134)" onmouseover="showTip(event, 'fs35', 134)" class="id">s2</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs6', 135)" onmouseover="showTip(event, 'fs6', 135)" class="id">N</span><span class="o">-</span><span class="n">1</span><span class="pn">)</span><span class="pn">)</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs7', 136)" onmouseover="showTip(event, 'fs7', 136)" class="fn">int</span>
<span class="pn">}</span>
<span class="c">/// Welch's t-test for a given Welch statistic to a confidence level of 0.1%.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs38', 137)" onmouseover="showTip(event, 'fs38', 137)" class="fn">welchTest</span> <span onmouseout="hideTip(event, 'fs39', 138)" onmouseover="showTip(event, 'fs39', 138)" class="id">w</span> <span class="o">=</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs40', 139)" onmouseover="showTip(event, 'fs40', 139)" class="fn">abs</span> <span onmouseout="hideTip(event, 'fs39', 140)" onmouseover="showTip(event, 'fs39', 140)" class="id">w</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs14', 141)" onmouseover="showTip(event, 'fs14', 141)" class="id">T</span> <span class="o"><</span> <span onmouseout="hideTip(event, 'fs41', 142)" onmouseover="showTip(event, 'fs41', 142)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs42', 143)" onmouseover="showTip(event, 'fs42', 143)" class="id">get</span> <span onmouseout="hideTip(event, 'fs32', 144)" onmouseover="showTip(event, 'fs32', 144)" class="id">tInv</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs43', 145)" onmouseover="showTip(event, 'fs43', 145)" class="fn">min</span> <span onmouseout="hideTip(event, 'fs39', 146)" onmouseover="showTip(event, 'fs39', 146)" class="id">w</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs15', 147)" onmouseover="showTip(event, 'fs15', 147)" class="id">DF</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs41', 148)" onmouseover="showTip(event, 'fs41', 148)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs44', 149)" onmouseover="showTip(event, 'fs44', 149)" class="id">length</span> <span onmouseout="hideTip(event, 'fs32', 150)" onmouseover="showTip(event, 'fs32', 150)" class="id">tInv</span><span class="pn">)</span> <span class="o">-</span> <span class="n">1</span><span class="pn">)</span> <span class="k">then</span> <span class="n">0</span>
<span class="k">else</span> <span onmouseout="hideTip(event, 'fs45', 151)" onmouseover="showTip(event, 'fs45', 151)" class="fn">sign</span> <span onmouseout="hideTip(event, 'fs39', 152)" onmouseover="showTip(event, 'fs39', 152)" class="id">w</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs14', 153)" onmouseover="showTip(event, 'fs14', 153)" class="id">T</span>
</code></pre>
<h2><a name="Performance-testing" class="anchor" href="#Performance-testing">Performance testing</a></h2>
<p>Three performance metrics will be created: time, memory allocated, and garbage collections.</p>
<p>Each of these will be repeated until at least the metric target is reached to ensure it is reliably measuring the metric and not any framework overhead.</p>
<p>Statistics functions will be created for each of the metrics to measure a function accurate to a mean standard error of 1%.</p>
<p>Compare functions will be created for each of the metrics to compare two functions to a confidence level of 0.1%.
The functions will be considered equal after 10,000 degrees of freedom have past without a positive test result.</p>
<h2><a name="Performance-testing-code" class="anchor" href="#Performance-testing-code">Performance testing code</a></h2>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="k">module</span> <span onmouseout="hideTip(event, 'fs47', 155)" onmouseover="showTip(event, 'fs47', 155)" class="m">Performance</span> <span class="o">=</span>
<span class="c">/// Find the iteration count to get to at least the metric target.</span>
<span class="k">let</span> <span class="k">inline</span> <span class="k">private</span> <span onmouseout="hideTip(event, 'fs48', 156)" onmouseover="showTip(event, 'fs48', 156)" class="fn">targetIterationCount</span> <span onmouseout="hideTip(event, 'fs49', 157)" onmouseover="showTip(event, 'fs49', 157)" class="fn">metric</span> <span onmouseout="hideTip(event, 'fs50', 158)" onmouseover="showTip(event, 'fs50', 158)" class="id">metricTarget</span> <span onmouseout="hideTip(event, 'fs51', 159)" onmouseover="showTip(event, 'fs51', 159)" class="id">f</span> <span class="o">=</span>
<span class="k">let</span> <span class="k">rec</span> <span onmouseout="hideTip(event, 'fs52', 160)" onmouseover="showTip(event, 'fs52', 160)" class="fn">find</span> <span onmouseout="hideTip(event, 'fs19', 161)" onmouseover="showTip(event, 'fs19', 161)" class="id">n</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs53', 162)" onmouseover="showTip(event, 'fs53', 162)" class="id">item</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs49', 163)" onmouseover="showTip(event, 'fs49', 163)" class="fn">metric</span> <span onmouseout="hideTip(event, 'fs19', 164)" onmouseover="showTip(event, 'fs19', 164)" class="id">n</span> <span onmouseout="hideTip(event, 'fs51', 165)" onmouseover="showTip(event, 'fs51', 165)" class="id">f</span>
<span class="k">if</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs53', 166)" onmouseover="showTip(event, 'fs53', 166)" class="id">item</span><span class="o"><<<</span><span class="n">3</span><span class="pn">)</span><span class="o">></span><span class="o">=</span><span onmouseout="hideTip(event, 'fs50', 167)" onmouseover="showTip(event, 'fs50', 167)" class="id">metricTarget</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs19', 168)" onmouseover="showTip(event, 'fs19', 168)" class="id">n</span><span class="o">*</span><span onmouseout="hideTip(event, 'fs7', 169)" onmouseover="showTip(event, 'fs7', 169)" class="fn">int</span> <span onmouseout="hideTip(event, 'fs50', 170)" onmouseover="showTip(event, 'fs50', 170)" class="id">metricTarget</span><span class="o">/</span><span onmouseout="hideTip(event, 'fs7', 171)" onmouseover="showTip(event, 'fs7', 171)" class="fn">int</span> <span onmouseout="hideTip(event, 'fs53', 172)" onmouseover="showTip(event, 'fs53', 172)" class="id">item</span><span class="o">+</span><span class="n">1</span>
<span class="k">else</span> <span onmouseout="hideTip(event, 'fs52', 173)" onmouseover="showTip(event, 'fs52', 173)" class="fn">find</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs19', 174)" onmouseover="showTip(event, 'fs19', 174)" class="id">n</span><span class="o">*</span><span class="n">10</span><span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs52', 175)" onmouseover="showTip(event, 'fs52', 175)" class="fn">find</span> <span class="n">1</span>
<span class="c">/// Create and iterate a statistics sequence for a metric until the given accuracy.</span>
<span class="k">let</span> <span class="k">inline</span> <span class="k">private</span> <span onmouseout="hideTip(event, 'fs54', 176)" onmouseover="showTip(event, 'fs54', 176)" class="fn">measureStatistics</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs55', 177)" onmouseover="showTip(event, 'fs55', 177)" class="fn">metric</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs56', 178)" onmouseover="showTip(event, 'fs56', 178)" class="id">metricTarget</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs57', 179)" onmouseover="showTip(event, 'fs57', 179)" class="id">relativeError</span> <span onmouseout="hideTip(event, 'fs51', 180)" onmouseover="showTip(event, 'fs51', 180)" class="id">f</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs31', 181)" onmouseover="showTip(event, 'fs31', 181)" class="id">ic</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs48', 182)" onmouseover="showTip(event, 'fs48', 182)" class="fn">targetIterationCount</span> <span onmouseout="hideTip(event, 'fs55', 183)" onmouseover="showTip(event, 'fs55', 183)" class="fn">metric</span> <span onmouseout="hideTip(event, 'fs56', 184)" onmouseover="showTip(event, 'fs56', 184)" class="id">metricTarget</span> <span onmouseout="hideTip(event, 'fs51', 185)" onmouseover="showTip(event, 'fs51', 185)" class="id">f</span>
<span onmouseout="hideTip(event, 'fs24', 186)" onmouseover="showTip(event, 'fs24', 186)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs58', 187)" onmouseover="showTip(event, 'fs58', 187)" class="id">initInfinite</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">_</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs55', 188)" onmouseover="showTip(event, 'fs55', 188)" class="fn">metric</span> <span onmouseout="hideTip(event, 'fs31', 189)" onmouseover="showTip(event, 'fs31', 189)" class="id">ic</span> <span onmouseout="hideTip(event, 'fs51', 190)" onmouseover="showTip(event, 'fs51', 190)" class="id">f</span><span class="pn">)</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs16', 191)" onmouseover="showTip(event, 'fs16', 191)" class="fn">sampleStatistics</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs24', 192)" onmouseover="showTip(event, 'fs24', 192)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs25', 193)" onmouseover="showTip(event, 'fs25', 193)" class="id">map</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs30', 194)" onmouseover="showTip(event, 'fs30', 194)" class="fn">singleIteration</span> <span onmouseout="hideTip(event, 'fs31', 195)" onmouseover="showTip(event, 'fs31', 195)" class="id">ic</span><span class="pn">)</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs24', 196)" onmouseover="showTip(event, 'fs24', 196)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs59', 197)" onmouseover="showTip(event, 'fs59', 197)" class="id">find</span> <span class="pn">(</span><span class="k">fun</span> <span onmouseout="hideTip(event, 'fs11', 198)" onmouseover="showTip(event, 'fs11', 198)" class="id">s</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs11', 199)" onmouseover="showTip(event, 'fs11', 199)" class="id">s</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs60', 200)" onmouseover="showTip(event, 'fs60', 200)" class="id">MeanStandardError</span><span class="o"><=</span><span onmouseout="hideTip(event, 'fs57', 201)" onmouseover="showTip(event, 'fs57', 201)" class="id">relativeError</span><span class="o">*</span><span onmouseout="hideTip(event, 'fs11', 202)" onmouseover="showTip(event, 'fs11', 202)" class="id">s</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs8', 203)" onmouseover="showTip(event, 'fs8', 203)" class="id">Mean</span><span class="pn">)</span>
<span class="c">/// Create and iterate two statistics sequences until the metric means can be</span>
<span class="c">/// compared.</span>
<span class="k">let</span> <span class="k">inline</span> <span class="k">private</span> <span onmouseout="hideTip(event, 'fs61', 204)" onmouseover="showTip(event, 'fs61', 204)" class="fn">measureCompare</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs62', 205)" onmouseover="showTip(event, 'fs62', 205)" class="fn">metric</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs63', 206)" onmouseover="showTip(event, 'fs63', 206)" class="id">metricTarget</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs64', 207)" onmouseover="showTip(event, 'fs64', 207)" class="fn">f1</span> <span onmouseout="hideTip(event, 'fs65', 208)" onmouseover="showTip(event, 'fs65', 208)" class="fn">f2</span> <span class="o">=</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs64', 209)" onmouseover="showTip(event, 'fs64', 209)" class="fn">f1</span> <span onmouseout="hideTip(event, 'fs66', 210)" onmouseover="showTip(event, 'fs66', 210)" class="fn">id</span><span class="o"><></span><span onmouseout="hideTip(event, 'fs65', 211)" onmouseover="showTip(event, 'fs65', 211)" class="fn">f2</span> <span onmouseout="hideTip(event, 'fs66', 212)" onmouseover="showTip(event, 'fs66', 212)" class="fn">id</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs67', 213)" onmouseover="showTip(event, 'fs67', 213)" class="fn">failwith</span> <span class="s">"function results are not the same"</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs31', 214)" onmouseover="showTip(event, 'fs31', 214)" class="id">ic</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs48', 215)" onmouseover="showTip(event, 'fs48', 215)" class="fn">targetIterationCount</span> <span onmouseout="hideTip(event, 'fs62', 216)" onmouseover="showTip(event, 'fs62', 216)" class="fn">metric</span> <span onmouseout="hideTip(event, 'fs63', 217)" onmouseover="showTip(event, 'fs63', 217)" class="id">metricTarget</span> <span onmouseout="hideTip(event, 'fs65', 218)" onmouseover="showTip(event, 'fs65', 218)" class="fn">f2</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs68', 219)" onmouseover="showTip(event, 'fs68', 219)" class="fn">stats</span> <span onmouseout="hideTip(event, 'fs69', 220)" onmouseover="showTip(event, 'fs69', 220)" class="fn">f</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs24', 221)" onmouseover="showTip(event, 'fs24', 221)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs58', 222)" onmouseover="showTip(event, 'fs58', 222)" class="id">initInfinite</span> <span class="pn">(</span><span class="k">fun</span> <span class="id">_</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs62', 223)" onmouseover="showTip(event, 'fs62', 223)" class="fn">metric</span> <span onmouseout="hideTip(event, 'fs31', 224)" onmouseover="showTip(event, 'fs31', 224)" class="id">ic</span> <span onmouseout="hideTip(event, 'fs69', 225)" onmouseover="showTip(event, 'fs69', 225)" class="fn">f</span><span class="pn">)</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs16', 226)" onmouseover="showTip(event, 'fs16', 226)" class="fn">sampleStatistics</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs24', 227)" onmouseover="showTip(event, 'fs24', 227)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs25', 228)" onmouseover="showTip(event, 'fs25', 228)" class="id">map</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs30', 229)" onmouseover="showTip(event, 'fs30', 229)" class="fn">singleIteration</span> <span onmouseout="hideTip(event, 'fs31', 230)" onmouseover="showTip(event, 'fs31', 230)" class="id">ic</span><span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs24', 231)" onmouseover="showTip(event, 'fs24', 231)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs70', 232)" onmouseover="showTip(event, 'fs70', 232)" class="id">map2</span> <span onmouseout="hideTip(event, 'fs33', 233)" onmouseover="showTip(event, 'fs33', 233)" class="fn">welchStatistic</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs68', 234)" onmouseover="showTip(event, 'fs68', 234)" class="fn">stats</span> <span onmouseout="hideTip(event, 'fs64', 235)" onmouseover="showTip(event, 'fs64', 235)" class="fn">f1</span><span class="pn">)</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs68', 236)" onmouseover="showTip(event, 'fs68', 236)" class="fn">stats</span> <span onmouseout="hideTip(event, 'fs65', 237)" onmouseover="showTip(event, 'fs65', 237)" class="fn">f2</span><span class="pn">)</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs24', 238)" onmouseover="showTip(event, 'fs24', 238)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs71', 239)" onmouseover="showTip(event, 'fs71', 239)" class="id">pick</span> <span class="pn">(</span><span class="k">fun</span> <span onmouseout="hideTip(event, 'fs39', 240)" onmouseover="showTip(event, 'fs39', 240)" class="id">w</span> <span class="k">-></span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs72', 241)" onmouseover="showTip(event, 'fs72', 241)" class="id">maxDF</span> <span class="o">=</span> <span class="n">10000</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs39', 242)" onmouseover="showTip(event, 'fs39', 242)" class="id">w</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs15', 243)" onmouseover="showTip(event, 'fs15', 243)" class="id">DF</span><span class="o">></span><span onmouseout="hideTip(event, 'fs72', 244)" onmouseover="showTip(event, 'fs72', 244)" class="id">maxDF</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs73', 245)" onmouseover="showTip(event, 'fs73', 245)" class="uc">Some</span> <span class="n">0</span> <span class="k">else</span> <span class="k">match</span> <span onmouseout="hideTip(event, 'fs38', 246)" onmouseover="showTip(event, 'fs38', 246)" class="fn">welchTest</span> <span onmouseout="hideTip(event, 'fs39', 247)" onmouseover="showTip(event, 'fs39', 247)" class="id">w</span> <span class="k">with</span> <span class="pn">|</span><span class="n">0</span><span class="k">-></span><span onmouseout="hideTip(event, 'fs74', 248)" onmouseover="showTip(event, 'fs74', 248)" class="uc">None</span> <span class="pn">|</span><span onmouseout="hideTip(event, 'fs75', 249)" onmouseover="showTip(event, 'fs75', 249)" class="id">c</span><span class="k">-></span><span onmouseout="hideTip(event, 'fs73', 250)" onmouseover="showTip(event, 'fs73', 250)" class="uc">Some</span> <span onmouseout="hideTip(event, 'fs75', 251)" onmouseover="showTip(event, 'fs75', 251)" class="id">c</span><span class="pn">)</span>
<span class="c">/// Measure the given function for the iteration count using the start and end</span>
<span class="c">/// metric.</span>
<span class="k">let</span> <span class="k">inline</span> <span class="k">private</span> <span onmouseout="hideTip(event, 'fs76', 252)" onmouseover="showTip(event, 'fs76', 252)" class="fn">measureMetric</span> <span onmouseout="hideTip(event, 'fs77', 253)" onmouseover="showTip(event, 'fs77', 253)" class="fn">startMetric</span> <span onmouseout="hideTip(event, 'fs78', 254)" onmouseover="showTip(event, 'fs78', 254)" class="fn">endMetric</span> <span onmouseout="hideTip(event, 'fs31', 255)" onmouseover="showTip(event, 'fs31', 255)" class="id">ic</span> <span onmouseout="hideTip(event, 'fs79', 256)" onmouseover="showTip(event, 'fs79', 256)" class="fn">f</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs80', 257)" onmouseover="showTip(event, 'fs80', 257)" class="rt">GC</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs81', 258)" onmouseover="showTip(event, 'fs81', 258)" class="id">Collect</span><span class="pn">(</span><span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs80', 259)" onmouseover="showTip(event, 'fs80', 259)" class="rt">GC</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs82', 260)" onmouseover="showTip(event, 'fs82', 260)" class="id">WaitForPendingFinalizers</span><span class="pn">(</span><span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs80', 261)" onmouseover="showTip(event, 'fs80', 261)" class="rt">GC</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs81', 262)" onmouseover="showTip(event, 'fs81', 262)" class="id">Collect</span><span class="pn">(</span><span class="pn">)</span>
<span class="k">let</span> <span class="k">mutable</span> <span onmouseout="hideTip(event, 'fs83', 263)" onmouseover="showTip(event, 'fs83', 263)" class="mv">total</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs84', 264)" onmouseover="showTip(event, 'fs84', 264)" class="m">LanguagePrimitives</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs85', 265)" onmouseover="showTip(event, 'fs85', 265)" class="id">GenericZero</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs86', 266)" onmouseover="showTip(event, 'fs86', 266)" class="fn">measurer</span> <span onmouseout="hideTip(event, 'fs87', 267)" onmouseover="showTip(event, 'fs87', 267)" class="fn">toMeasure</span> <span class="o">=</span>
<span class="k">fun</span> <span onmouseout="hideTip(event, 'fs88', 268)" onmouseover="showTip(event, 'fs88', 268)" class="id">args</span> <span class="k">-></span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs89', 269)" onmouseover="showTip(event, 'fs89', 269)" class="id">s</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs77', 270)" onmouseover="showTip(event, 'fs77', 270)" class="fn">startMetric</span><span class="pn">(</span><span class="pn">)</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs90', 271)" onmouseover="showTip(event, 'fs90', 271)" class="id">ret</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs87', 272)" onmouseover="showTip(event, 'fs87', 272)" class="fn">toMeasure</span> <span onmouseout="hideTip(event, 'fs88', 273)" onmouseover="showTip(event, 'fs88', 273)" class="id">args</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs91', 274)" onmouseover="showTip(event, 'fs91', 274)" class="id">m</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs78', 275)" onmouseover="showTip(event, 'fs78', 275)" class="fn">endMetric</span> <span onmouseout="hideTip(event, 'fs89', 276)" onmouseover="showTip(event, 'fs89', 276)" class="id">s</span>
<span onmouseout="hideTip(event, 'fs83', 277)" onmouseover="showTip(event, 'fs83', 277)" class="mv">total</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs83', 278)" onmouseover="showTip(event, 'fs83', 278)" class="mv">total</span><span class="o">+</span><span onmouseout="hideTip(event, 'fs91', 279)" onmouseover="showTip(event, 'fs91', 279)" class="id">m</span>
<span onmouseout="hideTip(event, 'fs90', 280)" onmouseover="showTip(event, 'fs90', 280)" class="id">ret</span>
<span class="k">let</span> <span class="k">rec</span> <span onmouseout="hideTip(event, 'fs92', 281)" onmouseover="showTip(event, 'fs92', 281)" class="fn">loop</span> <span onmouseout="hideTip(event, 'fs93', 282)" onmouseover="showTip(event, 'fs93', 282)" class="id">i</span> <span class="o">=</span> <span class="k">if</span> <span onmouseout="hideTip(event, 'fs93', 283)" onmouseover="showTip(event, 'fs93', 283)" class="id">i</span><span class="o">></span><span class="n">0</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs79', 284)" onmouseover="showTip(event, 'fs79', 284)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs86', 285)" onmouseover="showTip(event, 'fs86', 285)" class="fn">measurer</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs94', 286)" onmouseover="showTip(event, 'fs94', 286)" class="fn">ignore</span><span class="pn">;</span> <span onmouseout="hideTip(event, 'fs92', 287)" onmouseover="showTip(event, 'fs92', 287)" class="fn">loop</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs93', 288)" onmouseover="showTip(event, 'fs93', 288)" class="id">i</span><span class="o">-</span><span class="n">1</span><span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs92', 289)" onmouseover="showTip(event, 'fs92', 289)" class="fn">loop</span> <span onmouseout="hideTip(event, 'fs31', 290)" onmouseover="showTip(event, 'fs31', 290)" class="id">ic</span>
<span onmouseout="hideTip(event, 'fs83', 291)" onmouseover="showTip(event, 'fs83', 291)" class="mv">total</span>
<span class="c">/// Measure the time metric for the given function and iteration count.</span>
<span class="k">let</span> <span class="k">private</span> <span onmouseout="hideTip(event, 'fs95', 292)" onmouseover="showTip(event, 'fs95', 292)" class="fn">timeMetric</span> <span onmouseout="hideTip(event, 'fs31', 293)" onmouseover="showTip(event, 'fs31', 293)" class="id">ic</span> <span onmouseout="hideTip(event, 'fs96', 294)" onmouseover="showTip(event, 'fs96', 294)" class="fn">f</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs76', 295)" onmouseover="showTip(event, 'fs76', 295)" class="fn">measureMetric</span> <span onmouseout="hideTip(event, 'fs97', 296)" onmouseover="showTip(event, 'fs97', 296)" class="rt">Stopwatch</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs98', 297)" onmouseover="showTip(event, 'fs98', 297)" class="id">GetTimestamp</span> <span class="pn">(</span><span class="k">fun</span> <span onmouseout="hideTip(event, 'fs99', 298)" onmouseover="showTip(event, 'fs99', 298)" class="id">t</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs97', 299)" onmouseover="showTip(event, 'fs97', 299)" class="rt">Stopwatch</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs98', 300)" onmouseover="showTip(event, 'fs98', 300)" class="id">GetTimestamp</span><span class="pn">(</span><span class="pn">)</span><span class="o">-</span><span onmouseout="hideTip(event, 'fs99', 301)" onmouseover="showTip(event, 'fs99', 301)" class="id">t</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs31', 302)" onmouseover="showTip(event, 'fs31', 302)" class="id">ic</span> <span onmouseout="hideTip(event, 'fs96', 303)" onmouseover="showTip(event, 'fs96', 303)" class="fn">f</span>
<span class="c">/// Measure the memory metric for the given function and iteration count.</span>
<span class="k">let</span> <span class="k">private</span> <span onmouseout="hideTip(event, 'fs100', 304)" onmouseover="showTip(event, 'fs100', 304)" class="fn">memoryMetric</span> <span onmouseout="hideTip(event, 'fs31', 305)" onmouseover="showTip(event, 'fs31', 305)" class="id">ic</span> <span onmouseout="hideTip(event, 'fs96', 306)" onmouseover="showTip(event, 'fs96', 306)" class="fn">f</span> <span class="o">=</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs101', 307)" onmouseover="showTip(event, 'fs101', 307)" class="fn">startMetric</span><span class="pn">(</span><span class="pn">)</span> <span class="o">=</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs80', 308)" onmouseover="showTip(event, 'fs80', 308)" class="rt">GC</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs102', 309)" onmouseover="showTip(event, 'fs102', 309)" class="id">TryStartNoGCRegion</span><span class="pn">(</span><span class="n">1L</span><span class="o"><<<</span><span class="n">22</span><span class="pn">)</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs103', 310)" onmouseover="showTip(event, 'fs103', 310)" class="fn">not</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs67', 311)" onmouseover="showTip(event, 'fs67', 311)" class="fn">failwith</span> <span class="s">"TryStartNoGCRegion"</span>
<span onmouseout="hideTip(event, 'fs80', 312)" onmouseover="showTip(event, 'fs80', 312)" class="rt">GC</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs104', 313)" onmouseover="showTip(event, 'fs104', 313)" class="id">GetTotalMemory</span> <span class="k">false</span>
<span class="k">let</span> <span class="k">inline</span> <span onmouseout="hideTip(event, 'fs105', 314)" onmouseover="showTip(event, 'fs105', 314)" class="fn">endMetric</span> <span onmouseout="hideTip(event, 'fs106', 315)" onmouseover="showTip(event, 'fs106', 315)" class="id">s</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs99', 316)" onmouseover="showTip(event, 'fs99', 316)" class="id">t</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs80', 317)" onmouseover="showTip(event, 'fs80', 317)" class="rt">GC</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs104', 318)" onmouseover="showTip(event, 'fs104', 318)" class="id">GetTotalMemory</span> <span class="k">false</span> <span class="o">-</span> <span onmouseout="hideTip(event, 'fs106', 319)" onmouseover="showTip(event, 'fs106', 319)" class="id">s</span>
<span onmouseout="hideTip(event, 'fs80', 320)" onmouseover="showTip(event, 'fs80', 320)" class="rt">GC</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs107', 321)" onmouseover="showTip(event, 'fs107', 321)" class="id">EndNoGCRegion</span><span class="pn">(</span><span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs99', 322)" onmouseover="showTip(event, 'fs99', 322)" class="id">t</span>
<span onmouseout="hideTip(event, 'fs76', 323)" onmouseover="showTip(event, 'fs76', 323)" class="fn">measureMetric</span> <span onmouseout="hideTip(event, 'fs101', 324)" onmouseover="showTip(event, 'fs101', 324)" class="fn">startMetric</span> <span onmouseout="hideTip(event, 'fs105', 325)" onmouseover="showTip(event, 'fs105', 325)" class="fn">endMetric</span> <span onmouseout="hideTip(event, 'fs31', 326)" onmouseover="showTip(event, 'fs31', 326)" class="id">ic</span> <span onmouseout="hideTip(event, 'fs96', 327)" onmouseover="showTip(event, 'fs96', 327)" class="fn">f</span>
<span class="c">/// Measure the garbage collection metric for the given function and iteration</span>
<span class="c">/// count.</span>
<span class="k">let</span> <span class="k">private</span> <span onmouseout="hideTip(event, 'fs108', 328)" onmouseover="showTip(event, 'fs108', 328)" class="fn">garbageMetric</span> <span onmouseout="hideTip(event, 'fs31', 329)" onmouseover="showTip(event, 'fs31', 329)" class="id">ic</span> <span onmouseout="hideTip(event, 'fs96', 330)" onmouseover="showTip(event, 'fs96', 330)" class="fn">f</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs109', 331)" onmouseover="showTip(event, 'fs109', 331)" class="fn">count</span><span class="pn">(</span><span class="pn">)</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs80', 332)" onmouseover="showTip(event, 'fs80', 332)" class="rt">GC</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs110', 333)" onmouseover="showTip(event, 'fs110', 333)" class="id">CollectionCount</span> <span class="n">0</span> <span class="o">+</span> <span onmouseout="hideTip(event, 'fs80', 334)" onmouseover="showTip(event, 'fs80', 334)" class="rt">GC</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs110', 335)" onmouseover="showTip(event, 'fs110', 335)" class="id">CollectionCount</span> <span class="n">1</span> <span class="o">+</span> <span onmouseout="hideTip(event, 'fs80', 336)" onmouseover="showTip(event, 'fs80', 336)" class="rt">GC</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs110', 337)" onmouseover="showTip(event, 'fs110', 337)" class="id">CollectionCount</span> <span class="n">2</span>
<span onmouseout="hideTip(event, 'fs76', 338)" onmouseover="showTip(event, 'fs76', 338)" class="fn">measureMetric</span> <span onmouseout="hideTip(event, 'fs109', 339)" onmouseover="showTip(event, 'fs109', 339)" class="fn">count</span> <span class="pn">(</span><span class="k">fun</span> <span onmouseout="hideTip(event, 'fs111', 340)" onmouseover="showTip(event, 'fs111', 340)" class="id">s</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs109', 341)" onmouseover="showTip(event, 'fs109', 341)" class="fn">count</span><span class="pn">(</span><span class="pn">)</span><span class="o">-</span><span onmouseout="hideTip(event, 'fs111', 342)" onmouseover="showTip(event, 'fs111', 342)" class="id">s</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs31', 343)" onmouseover="showTip(event, 'fs31', 343)" class="id">ic</span> <span onmouseout="hideTip(event, 'fs96', 344)" onmouseover="showTip(event, 'fs96', 344)" class="fn">f</span>
<span class="c">/// Measure definitions which are a metric together with a metric target.</span>
<span class="k">let</span> <span class="k">private</span> <span onmouseout="hideTip(event, 'fs112', 345)" onmouseover="showTip(event, 'fs112', 345)" class="id">oneMillisecond</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs97', 346)" onmouseover="showTip(event, 'fs97', 346)" class="rt">Stopwatch</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs113', 347)" onmouseover="showTip(event, 'fs113', 347)" class="id">Frequency</span><span class="o">/</span><span class="n">1000L</span>
<span class="k">let</span> <span class="k">private</span> <span onmouseout="hideTip(event, 'fs114', 348)" onmouseover="showTip(event, 'fs114', 348)" class="id">timeMeasure</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs95', 349)" onmouseover="showTip(event, 'fs95', 349)" class="fn">timeMetric</span><span class="pn">,</span> <span onmouseout="hideTip(event, 'fs112', 350)" onmouseover="showTip(event, 'fs112', 350)" class="id">oneMillisecond</span>
<span class="k">let</span> <span class="k">private</span> <span onmouseout="hideTip(event, 'fs115', 351)" onmouseover="showTip(event, 'fs115', 351)" class="id">memoryMeasure</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs100', 352)" onmouseover="showTip(event, 'fs100', 352)" class="fn">memoryMetric</span><span class="pn">,</span> <span class="n">1024L</span> <span class="c">//1KB</span>
<span class="k">let</span> <span class="k">private</span> <span onmouseout="hideTip(event, 'fs116', 353)" onmouseover="showTip(event, 'fs116', 353)" class="id">garbageMeasure</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs108', 354)" onmouseover="showTip(event, 'fs108', 354)" class="fn">garbageMetric</span><span class="pn">,</span> <span class="n">10</span>
<span class="c">/// Time statistics for a given function accurate to a mean standard error of 1%.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs117', 355)" onmouseover="showTip(event, 'fs117', 355)" class="fn">timeStatistics</span> <span onmouseout="hideTip(event, 'fs96', 356)" onmouseover="showTip(event, 'fs96', 356)" class="fn">f</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs54', 357)" onmouseover="showTip(event, 'fs54', 357)" class="fn">measureStatistics</span> <span onmouseout="hideTip(event, 'fs114', 358)" onmouseover="showTip(event, 'fs114', 358)" class="id">timeMeasure</span> <span class="n">0.01</span> <span onmouseout="hideTip(event, 'fs96', 359)" onmouseover="showTip(event, 'fs96', 359)" class="fn">f</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs28', 360)" onmouseover="showTip(event, 'fs28', 360)" class="fn">scale</span> <span class="pn">(</span><span class="n">1.0</span><span class="o">/</span><span onmouseout="hideTip(event, 'fs9', 361)" onmouseover="showTip(event, 'fs9', 361)" class="fn">float</span> <span onmouseout="hideTip(event, 'fs97', 362)" onmouseover="showTip(event, 'fs97', 362)" class="rt">Stopwatch</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs113', 363)" onmouseover="showTip(event, 'fs113', 363)" class="id">Frequency</span><span class="pn">)</span>
<span class="c">/// Memory statistics for a given function accurate to a mean standard error of 1%.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs118', 364)" onmouseover="showTip(event, 'fs118', 364)" class="fn">memoryStatistics</span> <span onmouseout="hideTip(event, 'fs96', 365)" onmouseover="showTip(event, 'fs96', 365)" class="fn">f</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs54', 366)" onmouseover="showTip(event, 'fs54', 366)" class="fn">measureStatistics</span> <span onmouseout="hideTip(event, 'fs115', 367)" onmouseover="showTip(event, 'fs115', 367)" class="id">memoryMeasure</span> <span class="n">0.01</span> <span onmouseout="hideTip(event, 'fs96', 368)" onmouseover="showTip(event, 'fs96', 368)" class="fn">f</span>
<span class="c">/// GC count statistics for a given function accurate to a mean standard error of</span>
<span class="c">/// 1%.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs119', 369)" onmouseover="showTip(event, 'fs119', 369)" class="fn">gcCountStatistics</span> <span onmouseout="hideTip(event, 'fs96', 370)" onmouseover="showTip(event, 'fs96', 370)" class="fn">f</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs54', 371)" onmouseover="showTip(event, 'fs54', 371)" class="fn">measureStatistics</span> <span onmouseout="hideTip(event, 'fs116', 372)" onmouseover="showTip(event, 'fs116', 372)" class="id">garbageMeasure</span> <span class="n">0.01</span> <span onmouseout="hideTip(event, 'fs96', 373)" onmouseover="showTip(event, 'fs96', 373)" class="fn">f</span>
<span class="c">/// Time comparison for two given functions to a confidence level of 0.1%.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs120', 374)" onmouseover="showTip(event, 'fs120', 374)" class="fn">timeCompare</span> <span onmouseout="hideTip(event, 'fs121', 375)" onmouseover="showTip(event, 'fs121', 375)" class="fn">f1</span> <span onmouseout="hideTip(event, 'fs122', 376)" onmouseover="showTip(event, 'fs122', 376)" class="fn">f2</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs61', 377)" onmouseover="showTip(event, 'fs61', 377)" class="fn">measureCompare</span> <span onmouseout="hideTip(event, 'fs114', 378)" onmouseover="showTip(event, 'fs114', 378)" class="id">timeMeasure</span> <span onmouseout="hideTip(event, 'fs121', 379)" onmouseover="showTip(event, 'fs121', 379)" class="fn">f1</span> <span onmouseout="hideTip(event, 'fs122', 380)" onmouseover="showTip(event, 'fs122', 380)" class="fn">f2</span>
<span class="c">/// Memory comparison for two given functions to a confidence level of 0.1%.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs123', 381)" onmouseover="showTip(event, 'fs123', 381)" class="fn">memoryCompare</span> <span onmouseout="hideTip(event, 'fs121', 382)" onmouseover="showTip(event, 'fs121', 382)" class="fn">f1</span> <span onmouseout="hideTip(event, 'fs122', 383)" onmouseover="showTip(event, 'fs122', 383)" class="fn">f2</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs61', 384)" onmouseover="showTip(event, 'fs61', 384)" class="fn">measureCompare</span> <span onmouseout="hideTip(event, 'fs115', 385)" onmouseover="showTip(event, 'fs115', 385)" class="id">memoryMeasure</span> <span onmouseout="hideTip(event, 'fs121', 386)" onmouseover="showTip(event, 'fs121', 386)" class="fn">f1</span> <span onmouseout="hideTip(event, 'fs122', 387)" onmouseover="showTip(event, 'fs122', 387)" class="fn">f2</span>
<span class="c">/// GC count comparison for two given functions to a confidence level of 0.1%.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs124', 388)" onmouseover="showTip(event, 'fs124', 388)" class="fn">gcCountCompare</span> <span onmouseout="hideTip(event, 'fs121', 389)" onmouseover="showTip(event, 'fs121', 389)" class="fn">f1</span> <span onmouseout="hideTip(event, 'fs122', 390)" onmouseover="showTip(event, 'fs122', 390)" class="fn">f2</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs61', 391)" onmouseover="showTip(event, 'fs61', 391)" class="fn">measureCompare</span> <span onmouseout="hideTip(event, 'fs116', 392)" onmouseover="showTip(event, 'fs116', 392)" class="id">garbageMeasure</span> <span onmouseout="hideTip(event, 'fs121', 393)" onmouseover="showTip(event, 'fs121', 393)" class="fn">f1</span> <span onmouseout="hideTip(event, 'fs122', 394)" onmouseover="showTip(event, 'fs122', 394)" class="fn">f2</span>
</code></pre>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p>The performance testing functions have a very simple signature.
The statistics functions just take the function to be measured.
The compare functions just take the two functions to be compared.</p>
<p>The statistics functions give an overview of a function's performance.
These can easily be combined to produce a useful performance report.</p>
<p>The compare functions can be used in unit tests since they are a relative test and hence should be independent of the machine.
They are also fast since they stop as soon as the given confidence level is achieved.
The compare functions could also be extended to test if a function is a given percentage better than another.</p>
<p>Modularity from higher-order functions and lazy evaluation together with a little maths have produced a simple yet powerful performance testing library.</p>
<p>UPDATED (2016-10-21):
The functions have been extended to be able to measure a subfunction of the passed in functions.
This enables set up to be done without effecting the measurement.
The subfunction can be called multiple times and the metric will be aggregated.
An example of its use can be found in the Outliers and MAD <a href="/blog/2016/10/21/MAD-Outliers">post</a>.</p>
<p>UPDATED (2017-01-03):
Performance testing functionality has been added to the F# testing library <a href="https://github.com/haf/expecto">Expecto</a> using the code in this post.</p>
<div class="tip" id="fs1">namespace System</div>
<div class="tip" id="fs2">namespace System.Diagnostics</div>
<div class="tip" id="fs3">Multiple items<br />type AutoOpenAttribute =<br />  inherit Attribute<br />  new : unit -> AutoOpenAttribute<br />  new : path:string -> AutoOpenAttribute<br />  member Path : string<br /><br />--------------------<br />new : unit -> AutoOpenAttribute<br />new : path:string -> AutoOpenAttribute</div>
<div class="tip" id="fs4">val sqr : x:'a -> 'b (requires member ( * ))</div>
<div class="tip" id="fs5">val x : 'a (requires member ( * ))</div>
<div class="tip" id="fs6">SampleStatistics.N: int</div>
<div class="tip" id="fs7">Multiple items<br />val int : value:'T -> int (requires member op_Explicit)<br /><br />--------------------<br />type int = int32<br /><br />--------------------<br />type int<'Measure> = int</div>
<div class="tip" id="fs8">SampleStatistics.Mean: float</div>
<div class="tip" id="fs9">Multiple items<br />val float : value:'T -> float (requires member op_Explicit)<br /><br />--------------------<br />type float = Double<br /><br />--------------------<br />type float<'Measure> = float</div>
<div class="tip" id="fs10">SampleStatistics.Variance: float</div>
<div class="tip" id="fs11">val s : SampleStatistics</div>
<div class="tip" id="fs12">val sqrt : value:'T -> 'U (requires member Sqrt)</div>
<div class="tip" id="fs13">type WelchStatistic =<br />  {T: float;<br />   DF: int;}</div>
<div class="tip" id="fs14">WelchStatistic.T: float</div>
<div class="tip" id="fs15">WelchStatistic.DF: int</div>
<div class="tip" id="fs16">val sampleStatistics : s:seq<'a> -> seq<SampleStatistics> (requires member op_Explicit)<br /><em><br /><br /> Online statistics sequence for a given sample sequence.</em></div>
<div class="tip" id="fs17">val s : seq<'a> (requires member op_Explicit)</div>
<div class="tip" id="fs18">val calc : (int * float * float -> float -> int * float * float)</div>
<div class="tip" id="fs19">val n : int</div>
<div class="tip" id="fs20">val m : float</div>
<div class="tip" id="fs21">val s : float</div>
<div class="tip" id="fs22">val x : float</div>
<div class="tip" id="fs23">val m' : float</div>
<div class="tip" id="fs24">module Seq<br /><br />from Microsoft.FSharp.Collections</div>
<div class="tip" id="fs25">val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U></div>
<div class="tip" id="fs26">val scan : folder:('State -> 'T -> 'State) -> state:'State -> source:seq<'T> -> seq<'State></div>
<div class="tip" id="fs27">val skip : count:int -> source:seq<'T> -> seq<'T></div>
<div class="tip" id="fs28">val scale : f:float -> s:SampleStatistics -> SampleStatistics<br /><em><br /><br /> Scale the statistics for a given underlying random variable change of scale.</em></div>
<div class="tip" id="fs29">val f : float</div>
<div class="tip" id="fs30">val singleIteration : ic:int -> s:SampleStatistics -> SampleStatistics<br /><em><br /><br /> Single iteration statistics for a given iteration count and total statistics.</em></div>
<div class="tip" id="fs31">val ic : int</div>
<div class="tip" id="fs32">val private tInv : float []<br /><em><br /><br /> Student's t-distribution inverse for 0.1% confidence level by<br /> degrees of freedom.</em></div>
<div class="tip" id="fs33">val welchStatistic : s1:SampleStatistics -> s2:SampleStatistics -> WelchStatistic<br /><em><br /><br /> Welch's t-test statistic for two given sample statistics.</em></div>
<div class="tip" id="fs34">val s1 : SampleStatistics</div>
<div class="tip" id="fs35">val s2 : SampleStatistics</div>
<div class="tip" id="fs36">val f1 : float</div>
<div class="tip" id="fs37">val f2 : float</div>
<div class="tip" id="fs38">val welchTest : w:WelchStatistic -> int<br /><em><br /><br /> Welch's t-test for a given Welch statistic to a confidence level of 0.1%.</em></div>
<div class="tip" id="fs39">val w : WelchStatistic</div>
<div class="tip" id="fs40">val abs : value:'T -> 'T (requires member Abs)</div>
<div class="tip" id="fs41">type Array =<br />  member Clone : unit -> obj<br />  member CopyTo : array:Array * index:int -> unit + 1 overload<br />  member GetEnumerator : unit -> IEnumerator<br />  member GetLength : dimension:int -> int<br />  member GetLongLength : dimension:int -> int64<br />  member GetLowerBound : dimension:int -> int<br />  member GetUpperBound : dimension:int -> int<br />  member GetValue : [<ParamArray>] indices:int[] -> obj + 7 overloads<br />  member Initialize : unit -> unit<br />  member IsFixedSize : bool<br />  ...</div>
<div class="tip" id="fs42">val get : array:'T [] -> index:int -> 'T</div>
<div class="tip" id="fs43">val min : e1:'T -> e2:'T -> 'T (requires comparison)</div>
<div class="tip" id="fs44">val length : array:'T [] -> int</div>
<div class="tip" id="fs45">val sign : value:'T -> int (requires member get_Sign)</div>
<div class="tip" id="fs46">module Statistics<br /><br />from Main</div>
<div class="tip" id="fs47">module Performance<br /><br />from Main</div>
<div class="tip" id="fs48">val private targetIterationCount : metric:(int -> 'a -> 'b) -> metricTarget:'b -> f:'a -> int (requires member ( <<< ) and comparison and member op_Explicit)<br /><em><br /><br /> Find the iteration count to get to at least the metric target.</em></div>
<div class="tip" id="fs49">val metric : (int -> 'a -> 'b) (requires member ( <<< ) and comparison and member op_Explicit)</div>
<div class="tip" id="fs50">val metricTarget : 'b (requires member ( <<< ) and comparison and member op_Explicit)</div>
<div class="tip" id="fs51">val f : 'a</div>
<div class="tip" id="fs52">val find : (int -> int)</div>
<div class="tip" id="fs53">val item : 'b (requires member ( <<< ) and comparison and member op_Explicit)</div>
<div class="tip" id="fs54">val private measureStatistics : metric:(int -> 'a -> 'b) * metricTarget:'b -> relativeError:float -> f:'a -> SampleStatistics (requires member ( <<< ) and comparison and member op_Explicit and member op_Explicit)<br /><em><br /><br /> Create and iterate a statistics sequence for a metric until the given accuracy.</em></div>
<div class="tip" id="fs55">val metric : (int -> 'a -> 'b) (requires member ( <<< ) and comparison and member op_Explicit and member op_Explicit)</div>
<div class="tip" id="fs56">val metricTarget : 'b (requires member ( <<< ) and comparison and member op_Explicit and member op_Explicit)</div>
<div class="tip" id="fs57">val relativeError : float</div>
<div class="tip" id="fs58">val initInfinite : initializer:(int -> 'T) -> seq<'T></div>
<div class="tip" id="fs59">val find : predicate:('T -> bool) -> source:seq<'T> -> 'T</div>
<div class="tip" id="fs60">property SampleStatistics.MeanStandardError: float</div>
<div class="tip" id="fs61">val private measureCompare : metric:(int -> (('a -> 'a) -> 'b) -> 'c) * metricTarget:'c -> f1:(('a -> 'a) -> 'b) -> f2:(('a -> 'a) -> 'b) -> int (requires equality and member ( <<< ) and comparison and member op_Explicit and member op_Explicit)<br /><em><br /><br /> Create and iterate two statistics sequences until the metric means can be<br /> compared.</em></div>
<div class="tip" id="fs62">val metric : (int -> (('a -> 'a) -> 'b) -> 'c) (requires equality and member ( <<< ) and comparison and member op_Explicit and member op_Explicit)</div>
<div class="tip" id="fs63">val metricTarget : 'c (requires member ( <<< ) and comparison and member op_Explicit and member op_Explicit)</div>
<div class="tip" id="fs64">val f1 : (('a -> 'a) -> 'b) (requires equality)</div>
<div class="tip" id="fs65">val f2 : (('a -> 'a) -> 'b) (requires equality)</div>
<div class="tip" id="fs66">val id : x:'T -> 'T</div>
<div class="tip" id="fs67">val failwith : message:string -> 'T</div>
<div class="tip" id="fs68">val stats : ((('a -> 'a) -> 'b) -> seq<SampleStatistics>) (requires equality)</div>
<div class="tip" id="fs69">val f : (('a -> 'a) -> 'b) (requires equality)</div>
<div class="tip" id="fs70">val map2 : mapping:('T1 -> 'T2 -> 'U) -> source1:seq<'T1> -> source2:seq<'T2> -> seq<'U></div>
<div class="tip" id="fs71">val pick : chooser:('T -> 'U option) -> source:seq<'T> -> 'U</div>
<div class="tip" id="fs72">val maxDF : int</div>
<div class="tip" id="fs73">union case Option.Some: Value: 'T -> Option<'T></div>
<div class="tip" id="fs74">union case Option.None: Option<'T></div>
<div class="tip" id="fs75">val c : int</div>
<div class="tip" id="fs76">val private measureMetric : startMetric:(unit -> 'a) -> endMetric:('a -> 'b) -> ic:int -> f:((('d -> 'e) -> 'd -> 'e) -> 'f) -> 'c (requires member ( + ) and member get_Zero)<br /><em><br /><br /> Measure the given function for the iteration count using the start and end<br /> metric.</em></div>
<div class="tip" id="fs77">val startMetric : (unit -> 'a)</div>
<div class="tip" id="fs78">val endMetric : ('a -> 'b) (requires member ( + ) and member get_Zero)</div>
<div class="tip" id="fs79">val f : ((('d -> 'e) -> 'd -> 'e) -> 'f)</div>
<div class="tip" id="fs80">type GC =<br />  static member AddMemoryPressure : bytesAllocated:int64 -> unit<br />  static member CancelFullGCNotification : unit -> unit<br />  static member Collect : unit -> unit + 4 overloads<br />  static member CollectionCount : generation:int -> int<br />  static member EndNoGCRegion : unit -> unit<br />  static member GetGeneration : obj:obj -> int + 1 overload<br />  static member GetTotalMemory : forceFullCollection:bool -> int64<br />  static member KeepAlive : obj:obj -> unit<br />  static member MaxGeneration : int<br />  static member ReRegisterForFinalize : obj:obj -> unit<br />  ...</div>
<div class="tip" id="fs81">GC.Collect() : unit<br />GC.Collect(generation: int) : unit<br />GC.Collect(generation: int, mode: GCCollectionMode) : unit<br />GC.Collect(generation: int, mode: GCCollectionMode, blocking: bool) : unit<br />GC.Collect(generation: int, mode: GCCollectionMode, blocking: bool, compacting: bool) : unit</div>
<div class="tip" id="fs82">GC.WaitForPendingFinalizers() : unit</div>
<div class="tip" id="fs83">val mutable total : 'c (requires member get_Zero and member ( + ))</div>
<div class="tip" id="fs84">module LanguagePrimitives<br /><br />from Microsoft.FSharp.Core</div>
<div class="tip" id="fs85">val GenericZero<'T (requires member get_Zero)> : 'T (requires member get_Zero)</div>
<div class="tip" id="fs86">val measurer : (('g -> 'h) -> 'g -> 'h)</div>
<div class="tip" id="fs87">val toMeasure : ('g -> 'h)</div>
<div class="tip" id="fs88">val args : 'g</div>
<div class="tip" id="fs89">val s : 'a</div>
<div class="tip" id="fs90">val ret : 'h</div>
<div class="tip" id="fs91">val m : 'b (requires member ( + ) and member get_Zero)</div>
<div class="tip" id="fs92">val loop : (int -> unit)</div>
<div class="tip" id="fs93">val i : int</div>
<div class="tip" id="fs94">val ignore : value:'T -> unit</div>
<div class="tip" id="fs95">val private timeMetric : ic:int -> f:((('a -> 'b) -> 'a -> 'b) -> 'c) -> int64<br /><em><br /><br /> Measure the time metric for the given function and iteration count.</em></div>
<div class="tip" id="fs96">val f : ((('a -> 'b) -> 'a -> 'b) -> 'c)</div>
<div class="tip" id="fs97">Multiple items<br />type Stopwatch =<br />  new : unit -> Stopwatch<br />  member Elapsed : TimeSpan<br />  member ElapsedMilliseconds : int64<br />  member ElapsedTicks : int64<br />  member IsRunning : bool<br />  member Reset : unit -> unit<br />  member Restart : unit -> unit<br />  member Start : unit -> unit<br />  member Stop : unit -> unit<br />  static val Frequency : int64<br />  ...<br /><br />--------------------<br />Stopwatch() : Stopwatch</div>
<div class="tip" id="fs98">Stopwatch.GetTimestamp() : int64</div>
<div class="tip" id="fs99">val t : int64</div>
<div class="tip" id="fs100">val private memoryMetric : ic:int -> f:((('a -> 'b) -> 'a -> 'b) -> 'c) -> int64<br /><em><br /><br /> Measure the memory metric for the given function and iteration count.</em></div>
<div class="tip" id="fs101">val startMetric : (unit -> int64)</div>
<div class="tip" id="fs102">GC.TryStartNoGCRegion(totalSize: int64) : bool<br />GC.TryStartNoGCRegion(totalSize: int64, disallowFullBlockingGC: bool) : bool<br />GC.TryStartNoGCRegion(totalSize: int64, lohSize: int64) : bool<br />GC.TryStartNoGCRegion(totalSize: int64, lohSize: int64, disallowFullBlockingGC: bool) : bool</div>
<div class="tip" id="fs103">val not : value:bool -> bool</div>
<div class="tip" id="fs104">GC.GetTotalMemory(forceFullCollection: bool) : int64</div>
<div class="tip" id="fs105">val endMetric : (int64 -> int64)</div>
<div class="tip" id="fs106">val s : int64</div>
<div class="tip" id="fs107">GC.EndNoGCRegion() : unit</div>
<div class="tip" id="fs108">val private garbageMetric : ic:int -> f:((('a -> 'b) -> 'a -> 'b) -> 'c) -> int<br /><em><br /><br /> Measure the garbage collection metric for the given function and iteration<br /> count.</em></div>
<div class="tip" id="fs109">val count : (unit -> int)</div>
<div class="tip" id="fs110">GC.CollectionCount(generation: int) : int</div>
<div class="tip" id="fs111">val s : int</div>
<div class="tip" id="fs112">val private oneMillisecond : int64<br /><em><br /><br /> Measure definitions which are a metric together with a metric target.</em></div>
<div class="tip" id="fs113">field Stopwatch.Frequency: int64</div>
<div class="tip" id="fs114">val private timeMeasure : (int -> ((('a -> 'b) -> 'a -> 'b) -> 'c) -> int64) * int64</div>
<div class="tip" id="fs115">val private memoryMeasure : (int -> ((('a -> 'b) -> 'a -> 'b) -> 'c) -> int64) * int64</div>
<div class="tip" id="fs116">val private garbageMeasure : (int -> ((('a -> 'b) -> 'a -> 'b) -> 'c) -> int) * int</div>
<div class="tip" id="fs117">val timeStatistics : f:((('a -> 'b) -> 'a -> 'b) -> 'c) -> SampleStatistics<br /><em><br /><br /> Time statistics for a given function accurate to a mean standard error of 1%.</em></div>
<div class="tip" id="fs118">val memoryStatistics : f:((('a -> 'b) -> 'a -> 'b) -> 'c) -> SampleStatistics<br /><em><br /><br /> Memory statistics for a given function accurate to a mean standard error of 1%.</em></div>
<div class="tip" id="fs119">val gcCountStatistics : f:((('a -> 'b) -> 'a -> 'b) -> 'c) -> SampleStatistics<br /><em><br /><br /> GC count statistics for a given function accurate to a mean standard error of<br /> 1%.</em></div>
<div class="tip" id="fs120">val timeCompare : f1:((('a -> 'b) -> 'a -> 'b) -> 'c) -> f2:((('a -> 'b) -> 'a -> 'b) -> 'c) -> int (requires equality)<br /><em><br /><br /> Time comparison for two given functions to a confidence level of 0.1%.</em></div>
<div class="tip" id="fs121">val f1 : ((('a -> 'b) -> 'a -> 'b) -> 'c) (requires equality)</div>
<div class="tip" id="fs122">val f2 : ((('a -> 'b) -> 'a -> 'b) -> 'c) (requires equality)</div>
<div class="tip" id="fs123">val memoryCompare : f1:((('a -> 'b) -> 'a -> 'b) -> 'c) -> f2:((('a -> 'b) -> 'a -> 'b) -> 'c) -> int (requires equality)<br /><em><br /><br /> Memory comparison for two given functions to a confidence level of 0.1%.</em></div>
<div class="tip" id="fs124">val gcCountCompare : f1:((('a -> 'b) -> 'a -> 'b) -> 'c) -> f2:((('a -> 'b) -> 'a -> 'b) -> 'c) -> int (requires equality)<br /><em><br /><br /> GC count comparison for two given functions to a confidence level of 0.1%.</em></div>
Modularity from Lazy Evaluation - Richardson Extrapolation
2016-04-27T00:00:00+01:00
http://anthonylloyd.github.io/blog/2016/04/27/fsharp-modularity-example
<p>This is an F# example of how higher-order functions together with lazy evaluation can reduce complexity and lead to more modular software.</p>
<h2><a name="Background" class="anchor" href="#Background">Background</a></h2>
<p>Richardson extrapolation is a method of combining multiple function estimates to increase estimate accuracy.
This post will cover estimating the derivative and the integral of a function to an arbitrary accuracy.</p>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="c">/// Derivative estimate.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs22', 49)" onmouseover="showTip(event, 'fs22', 49)" class="fn">derivativeEstimate</span> <span onmouseout="hideTip(event, 'fs23', 50)" onmouseover="showTip(event, 'fs23', 50)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs24', 51)" onmouseover="showTip(event, 'fs24', 51)" class="id">x</span> <span onmouseout="hideTip(event, 'fs25', 52)" onmouseover="showTip(event, 'fs25', 52)" class="id">h</span> <span class="o">=</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs23', 53)" onmouseover="showTip(event, 'fs23', 53)" class="fn">f</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs24', 54)" onmouseover="showTip(event, 'fs24', 54)" class="id">x</span><span class="o">+</span><span onmouseout="hideTip(event, 'fs25', 55)" onmouseover="showTip(event, 'fs25', 55)" class="id">h</span><span class="pn">)</span><span class="o">-</span><span onmouseout="hideTip(event, 'fs23', 56)" onmouseover="showTip(event, 'fs23', 56)" class="fn">f</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs24', 57)" onmouseover="showTip(event, 'fs24', 57)" class="id">x</span><span class="o">-</span><span onmouseout="hideTip(event, 'fs25', 58)" onmouseover="showTip(event, 'fs25', 58)" class="id">h</span><span class="pn">)</span><span class="pn">)</span><span class="o">/</span><span onmouseout="hideTip(event, 'fs25', 59)" onmouseover="showTip(event, 'fs25', 59)" class="id">h</span><span class="o">*</span><span class="n">0.5</span>
<span class="c">/// Integral estimate (h = (b-a)/n where n is an integer).</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs26', 60)" onmouseover="showTip(event, 'fs26', 60)" class="fn">integralEstimate</span> <span onmouseout="hideTip(event, 'fs23', 61)" onmouseover="showTip(event, 'fs23', 61)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs27', 62)" onmouseover="showTip(event, 'fs27', 62)" class="id">a</span> <span onmouseout="hideTip(event, 'fs28', 63)" onmouseover="showTip(event, 'fs28', 63)" class="id">b</span> <span onmouseout="hideTip(event, 'fs25', 64)" onmouseover="showTip(event, 'fs25', 64)" class="id">h</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs25', 65)" onmouseover="showTip(event, 'fs25', 65)" class="id">h</span><span class="o">*</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs23', 66)" onmouseover="showTip(event, 'fs23', 66)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs27', 67)" onmouseover="showTip(event, 'fs27', 67)" class="id">a</span><span class="o">+</span><span onmouseout="hideTip(event, 'fs23', 68)" onmouseover="showTip(event, 'fs23', 68)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs28', 69)" onmouseover="showTip(event, 'fs28', 69)" class="id">b</span><span class="pn">)</span><span class="o">*</span><span class="n">0.5</span> <span class="o">+</span> <span onmouseout="hideTip(event, 'fs25', 70)" onmouseover="showTip(event, 'fs25', 70)" class="id">h</span><span class="o">*</span><span onmouseout="hideTip(event, 'fs29', 71)" onmouseover="showTip(event, 'fs29', 71)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs30', 72)" onmouseover="showTip(event, 'fs30', 72)" class="id">sumBy</span> <span onmouseout="hideTip(event, 'fs23', 73)" onmouseover="showTip(event, 'fs23', 73)" class="fn">f</span> <span class="pn">{</span><span onmouseout="hideTip(event, 'fs27', 74)" onmouseover="showTip(event, 'fs27', 74)" class="id">a</span><span class="o">+</span><span onmouseout="hideTip(event, 'fs25', 75)" onmouseover="showTip(event, 'fs25', 75)" class="id">h</span><span class="o">..</span><span onmouseout="hideTip(event, 'fs25', 76)" onmouseover="showTip(event, 'fs25', 76)" class="id">h</span><span class="o">*</span><span class="n">2.0</span><span class="o">..</span><span onmouseout="hideTip(event, 'fs28', 77)" onmouseover="showTip(event, 'fs28', 77)" class="id">b</span><span class="pn">}</span>
</code></pre>
<p>Both the derivative and integral estimate can be shown to have an error term that has even powers of <span class="math">\(h\)</span>.</p>
<p><span class="math">\[Actual = Estimate(h) + e_1 h^2 + e_2 h^4 + \cdots\]</span></p>
<p>Richardson extrapolation combines multiple estimates to eliminate the lower power error terms.</p>
<p><span class="math">\[R_{ij} =
\left\{
\begin{align}
& Estimate\left(\frac{h}{2^i}\right) & j=0 \\
& \frac{4^j R_{i,j-1} - R_{i-1,j-1}}{4^j-1} & i \geq j, \, j > 0 \\
\end{align}
\right.\]</span></p>
<p>For small <span class="math">\(h\)</span> this rapidly improves the accuracy of the estimate.</p>
<p><span class="math">\[Actual = R_{ij} + \hat{e}_1 h^{2j+2} + \hat{e}_2 h^{2j+4} + \cdots\]</span></p>
<p>This produces a triangle of improving estimates.</p>
<p><span class="math">\[\begin{matrix}
R_{00} & \\
& \searrow & \\
R_{10} & \rightarrow & R_{11} \\
& \searrow & & \searrow \\
R_{20} & \rightarrow & R_{21} & \rightarrow & R_{22} \\
\vdots & & \vdots & & \vdots & \ddots \\
\end{matrix}\]</span></p>
<p>The stopping criteria is usually that <span class="math">\(|R_{n-2,n-2}-R_{n-1,n-1}|\)</span> and <span class="math">\(|R_{n-1,n-1}-R_{n,n}|\)</span> are within a desired accuracy.</p>
<h2><a name="First-attempt" class="anchor" href="#First-attempt">First attempt</a></h2>
<p>The first implementation will be attempted without using functional techniques.</p>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="c">/// Stopping criteria for a given accuracy and list of Richardson estimates.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs31', 78)" onmouseover="showTip(event, 'fs31', 78)" class="fn">stoppingCriteriaNonFunctional</span> <span onmouseout="hideTip(event, 'fs32', 79)" onmouseover="showTip(event, 'fs32', 79)" class="id">tol</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs33', 80)" onmouseover="showTip(event, 'fs33', 80)" class="id">rows</span><span class="pn">:</span><span onmouseout="hideTip(event, 'fs34', 81)" onmouseover="showTip(event, 'fs34', 81)" class="rt">List</span><span class="pn"><</span><span onmouseout="hideTip(event, 'fs35', 82)" onmouseover="showTip(event, 'fs35', 82)" class="vt">float</span> <span onmouseout="hideTip(event, 'fs36', 83)" onmouseover="showTip(event, 'fs36', 83)" class="rt">array</span><span class="pn">></span><span class="pn">)</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs37', 84)" onmouseover="showTip(event, 'fs37', 84)" class="id">c</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs33', 85)" onmouseover="showTip(event, 'fs33', 85)" class="id">rows</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs38', 86)" onmouseover="showTip(event, 'fs38', 86)" class="id">Count</span>
<span onmouseout="hideTip(event, 'fs37', 87)" onmouseover="showTip(event, 'fs37', 87)" class="id">c</span><span class="o">></span><span class="n">2</span> <span class="o">&&</span>
<span onmouseout="hideTip(event, 'fs39', 88)" onmouseover="showTip(event, 'fs39', 88)" class="fn">abs</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs33', 89)" onmouseover="showTip(event, 'fs33', 89)" class="id">rows</span><span class="pn">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs37', 90)" onmouseover="showTip(event, 'fs37', 90)" class="id">c</span><span class="o">-</span><span class="n">3</span><span class="pn">]</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs37', 91)" onmouseover="showTip(event, 'fs37', 91)" class="id">c</span><span class="o">-</span><span class="n">3</span><span class="pn">]</span><span class="o">-</span><span onmouseout="hideTip(event, 'fs33', 92)" onmouseover="showTip(event, 'fs33', 92)" class="id">rows</span><span class="pn">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs37', 93)" onmouseover="showTip(event, 'fs37', 93)" class="id">c</span><span class="o">-</span><span class="n">2</span><span class="pn">]</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs37', 94)" onmouseover="showTip(event, 'fs37', 94)" class="id">c</span><span class="o">-</span><span class="n">2</span><span class="pn">]</span><span class="pn">)</span><span class="o"><=</span><span onmouseout="hideTip(event, 'fs32', 95)" onmouseover="showTip(event, 'fs32', 95)" class="id">tol</span> <span class="o">&&</span>
<span onmouseout="hideTip(event, 'fs39', 96)" onmouseover="showTip(event, 'fs39', 96)" class="fn">abs</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs33', 97)" onmouseover="showTip(event, 'fs33', 97)" class="id">rows</span><span class="pn">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs37', 98)" onmouseover="showTip(event, 'fs37', 98)" class="id">c</span><span class="o">-</span><span class="n">2</span><span class="pn">]</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs37', 99)" onmouseover="showTip(event, 'fs37', 99)" class="id">c</span><span class="o">-</span><span class="n">2</span><span class="pn">]</span><span class="o">-</span><span onmouseout="hideTip(event, 'fs33', 100)" onmouseover="showTip(event, 'fs33', 100)" class="id">rows</span><span class="pn">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs37', 101)" onmouseover="showTip(event, 'fs37', 101)" class="id">c</span><span class="o">-</span><span class="n">1</span><span class="pn">]</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs37', 102)" onmouseover="showTip(event, 'fs37', 102)" class="id">c</span><span class="o">-</span><span class="n">1</span><span class="pn">]</span><span class="pn">)</span><span class="o"><=</span><span onmouseout="hideTip(event, 'fs32', 103)" onmouseover="showTip(event, 'fs32', 103)" class="id">tol</span>
<span class="c">/// The Richardson formula for a function estimate that has even power error terms.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs40', 104)" onmouseover="showTip(event, 'fs40', 104)" class="fn">richardsonFormula</span> <span onmouseout="hideTip(event, 'fs41', 105)" onmouseover="showTip(event, 'fs41', 105)" class="id">currentRowR</span> <span onmouseout="hideTip(event, 'fs42', 106)" onmouseover="showTip(event, 'fs42', 106)" class="id">previousRowR</span> <span onmouseout="hideTip(event, 'fs43', 107)" onmouseover="showTip(event, 'fs43', 107)" class="id">pow4</span> <span class="o">=</span>
<span class="pn">(</span><span onmouseout="hideTip(event, 'fs41', 108)" onmouseover="showTip(event, 'fs41', 108)" class="id">currentRowR</span><span class="o">*</span><span onmouseout="hideTip(event, 'fs43', 109)" onmouseover="showTip(event, 'fs43', 109)" class="id">pow4</span><span class="o">-</span><span onmouseout="hideTip(event, 'fs42', 110)" onmouseover="showTip(event, 'fs42', 110)" class="id">previousRowR</span><span class="pn">)</span><span class="o">/</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs43', 111)" onmouseover="showTip(event, 'fs43', 111)" class="id">pow4</span><span class="o">-</span><span class="n">1.0</span><span class="pn">)</span>
<span class="c">/// Derivative accurate to tol using Richardson extrapolation.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs44', 112)" onmouseover="showTip(event, 'fs44', 112)" class="fn">derivativeNonFunctional</span> <span onmouseout="hideTip(event, 'fs32', 113)" onmouseover="showTip(event, 'fs32', 113)" class="id">tol</span> <span onmouseout="hideTip(event, 'fs45', 114)" onmouseover="showTip(event, 'fs45', 114)" class="id">h0</span> <span onmouseout="hideTip(event, 'fs23', 115)" onmouseover="showTip(event, 'fs23', 115)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs24', 116)" onmouseover="showTip(event, 'fs24', 116)" class="id">x</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs46', 117)" onmouseover="showTip(event, 'fs46', 117)" class="id">richardsonRows</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs34', 118)" onmouseover="showTip(event, 'fs34', 118)" class="rt">List</span><span class="pn"><</span><span onmouseout="hideTip(event, 'fs35', 119)" onmouseover="showTip(event, 'fs35', 119)" class="vt">float</span> <span onmouseout="hideTip(event, 'fs36', 120)" onmouseover="showTip(event, 'fs36', 120)" class="rt">array</span><span class="pn">></span><span class="pn">(</span><span class="pn">)</span>
<span class="k">let</span> <span class="k">mutable</span> <span onmouseout="hideTip(event, 'fs47', 121)" onmouseover="showTip(event, 'fs47', 121)" class="mv">h</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs45', 122)" onmouseover="showTip(event, 'fs45', 122)" class="id">h0</span><span class="o">*</span><span class="n">0.5</span>
<span onmouseout="hideTip(event, 'fs46', 123)" onmouseover="showTip(event, 'fs46', 123)" class="fn">richardsonRows</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs48', 124)" onmouseover="showTip(event, 'fs48', 124)" class="id">Add</span> <span class="pn">(</span><span class="pn">[|</span><span onmouseout="hideTip(event, 'fs22', 125)" onmouseover="showTip(event, 'fs22', 125)" class="fn">derivativeEstimate</span> <span onmouseout="hideTip(event, 'fs23', 126)" onmouseover="showTip(event, 'fs23', 126)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs24', 127)" onmouseover="showTip(event, 'fs24', 127)" class="id">x</span> <span onmouseout="hideTip(event, 'fs45', 128)" onmouseover="showTip(event, 'fs45', 128)" class="id">h0</span><span class="pn">|]</span><span class="pn">)</span>
<span class="k">let</span> <span class="k">rec</span> <span onmouseout="hideTip(event, 'fs49', 129)" onmouseover="showTip(event, 'fs49', 129)" class="fn">run</span> <span class="pn">(</span><span class="pn">)</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs50', 130)" onmouseover="showTip(event, 'fs50', 130)" class="id">lastRow</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs29', 131)" onmouseover="showTip(event, 'fs29', 131)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs51', 132)" onmouseover="showTip(event, 'fs51', 132)" class="id">last</span> <span onmouseout="hideTip(event, 'fs46', 133)" onmouseover="showTip(event, 'fs46', 133)" class="id">richardsonRows</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs52', 134)" onmouseover="showTip(event, 'fs52', 134)" class="id">row</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs53', 135)" onmouseover="showTip(event, 'fs53', 135)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs54', 136)" onmouseover="showTip(event, 'fs54', 136)" class="id">zeroCreate</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs53', 137)" onmouseover="showTip(event, 'fs53', 137)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs55', 138)" onmouseover="showTip(event, 'fs55', 138)" class="id">length</span> <span onmouseout="hideTip(event, 'fs50', 139)" onmouseover="showTip(event, 'fs50', 139)" class="id">lastRow</span><span class="o">+</span><span class="n">1</span><span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs52', 140)" onmouseover="showTip(event, 'fs52', 140)" class="id">row</span><span class="m">.</span><span class="pn">[</span><span class="n">0</span><span class="pn">]</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs22', 141)" onmouseover="showTip(event, 'fs22', 141)" class="fn">derivativeEstimate</span> <span onmouseout="hideTip(event, 'fs23', 142)" onmouseover="showTip(event, 'fs23', 142)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs24', 143)" onmouseover="showTip(event, 'fs24', 143)" class="id">x</span> <span onmouseout="hideTip(event, 'fs47', 144)" onmouseover="showTip(event, 'fs47', 144)" class="mv">h</span>
<span class="k">let</span> <span class="k">mutable</span> <span onmouseout="hideTip(event, 'fs56', 145)" onmouseover="showTip(event, 'fs56', 145)" class="mv">pow4</span> <span class="o">=</span> <span class="n">4.0</span>
<span class="k">for</span> <span onmouseout="hideTip(event, 'fs57', 146)" onmouseover="showTip(event, 'fs57', 146)" class="id">i</span> <span class="o">=</span> <span class="n">0</span> <span class="k">to</span> <span onmouseout="hideTip(event, 'fs53', 147)" onmouseover="showTip(event, 'fs53', 147)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs55', 148)" onmouseover="showTip(event, 'fs55', 148)" class="id">length</span> <span onmouseout="hideTip(event, 'fs50', 149)" onmouseover="showTip(event, 'fs50', 149)" class="id">lastRow</span><span class="o">-</span><span class="n">1</span> <span class="k">do</span>
<span onmouseout="hideTip(event, 'fs52', 150)" onmouseover="showTip(event, 'fs52', 150)" class="id">row</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs57', 151)" onmouseover="showTip(event, 'fs57', 151)" class="id">i</span><span class="o">+</span><span class="n">1</span><span class="pn">]</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs40', 152)" onmouseover="showTip(event, 'fs40', 152)" class="fn">richardsonFormula</span> <span onmouseout="hideTip(event, 'fs52', 153)" onmouseover="showTip(event, 'fs52', 153)" class="id">row</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs57', 154)" onmouseover="showTip(event, 'fs57', 154)" class="id">i</span><span class="pn">]</span> <span onmouseout="hideTip(event, 'fs50', 155)" onmouseover="showTip(event, 'fs50', 155)" class="id">lastRow</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs57', 156)" onmouseover="showTip(event, 'fs57', 156)" class="id">i</span><span class="pn">]</span> <span onmouseout="hideTip(event, 'fs56', 157)" onmouseover="showTip(event, 'fs56', 157)" class="mv">pow4</span>
<span onmouseout="hideTip(event, 'fs56', 158)" onmouseover="showTip(event, 'fs56', 158)" class="mv">pow4</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs56', 159)" onmouseover="showTip(event, 'fs56', 159)" class="mv">pow4</span><span class="o">*</span><span class="n">4.0</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs31', 160)" onmouseover="showTip(event, 'fs31', 160)" class="fn">stoppingCriteriaNonFunctional</span> <span onmouseout="hideTip(event, 'fs32', 161)" onmouseover="showTip(event, 'fs32', 161)" class="id">tol</span> <span onmouseout="hideTip(event, 'fs46', 162)" onmouseover="showTip(event, 'fs46', 162)" class="id">richardsonRows</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs53', 163)" onmouseover="showTip(event, 'fs53', 163)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs58', 164)" onmouseover="showTip(event, 'fs58', 164)" class="id">last</span> <span onmouseout="hideTip(event, 'fs50', 165)" onmouseover="showTip(event, 'fs50', 165)" class="id">lastRow</span>
<span class="k">else</span>
<span onmouseout="hideTip(event, 'fs46', 166)" onmouseover="showTip(event, 'fs46', 166)" class="fn">richardsonRows</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs48', 167)" onmouseover="showTip(event, 'fs48', 167)" class="id">Add</span> <span onmouseout="hideTip(event, 'fs52', 168)" onmouseover="showTip(event, 'fs52', 168)" class="id">row</span>
<span onmouseout="hideTip(event, 'fs47', 169)" onmouseover="showTip(event, 'fs47', 169)" class="mv">h</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs47', 170)" onmouseover="showTip(event, 'fs47', 170)" class="mv">h</span><span class="o">*</span><span class="n">0.5</span>
<span onmouseout="hideTip(event, 'fs49', 171)" onmouseover="showTip(event, 'fs49', 171)" class="fn">run</span><span class="pn">(</span><span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs49', 172)" onmouseover="showTip(event, 'fs49', 172)" class="fn">run</span><span class="pn">(</span><span class="pn">)</span>
<span class="c">/// Iterative integral estimate (h is half the value used in the previous estimate).</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs59', 173)" onmouseover="showTip(event, 'fs59', 173)" class="fn">integralEstimateIterative</span> <span onmouseout="hideTip(event, 'fs23', 174)" onmouseover="showTip(event, 'fs23', 174)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs27', 175)" onmouseover="showTip(event, 'fs27', 175)" class="id">a</span> <span onmouseout="hideTip(event, 'fs28', 176)" onmouseover="showTip(event, 'fs28', 176)" class="id">b</span> <span onmouseout="hideTip(event, 'fs60', 177)" onmouseover="showTip(event, 'fs60', 177)" class="id">previousEstimate</span> <span onmouseout="hideTip(event, 'fs25', 178)" onmouseover="showTip(event, 'fs25', 178)" class="id">h</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs60', 179)" onmouseover="showTip(event, 'fs60', 179)" class="id">previousEstimate</span><span class="o">*</span><span class="n">0.5</span><span class="o">+</span><span onmouseout="hideTip(event, 'fs25', 180)" onmouseover="showTip(event, 'fs25', 180)" class="id">h</span><span class="o">*</span><span onmouseout="hideTip(event, 'fs29', 181)" onmouseover="showTip(event, 'fs29', 181)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs30', 182)" onmouseover="showTip(event, 'fs30', 182)" class="id">sumBy</span> <span onmouseout="hideTip(event, 'fs23', 183)" onmouseover="showTip(event, 'fs23', 183)" class="fn">f</span> <span class="pn">{</span><span onmouseout="hideTip(event, 'fs27', 184)" onmouseover="showTip(event, 'fs27', 184)" class="id">a</span><span class="o">+</span><span onmouseout="hideTip(event, 'fs25', 185)" onmouseover="showTip(event, 'fs25', 185)" class="id">h</span><span class="o">..</span><span onmouseout="hideTip(event, 'fs25', 186)" onmouseover="showTip(event, 'fs25', 186)" class="id">h</span><span class="o">*</span><span class="n">2.0</span><span class="o">..</span><span onmouseout="hideTip(event, 'fs28', 187)" onmouseover="showTip(event, 'fs28', 187)" class="id">b</span><span class="pn">}</span>
<span class="c">/// Integral accurate to tol using Richardson extrapolation.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs61', 188)" onmouseover="showTip(event, 'fs61', 188)" class="fn">integralNonFunctional</span> <span onmouseout="hideTip(event, 'fs32', 189)" onmouseover="showTip(event, 'fs32', 189)" class="id">tol</span> <span onmouseout="hideTip(event, 'fs23', 190)" onmouseover="showTip(event, 'fs23', 190)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs27', 191)" onmouseover="showTip(event, 'fs27', 191)" class="id">a</span> <span onmouseout="hideTip(event, 'fs28', 192)" onmouseover="showTip(event, 'fs28', 192)" class="id">b</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs46', 193)" onmouseover="showTip(event, 'fs46', 193)" class="id">richardsonRows</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs34', 194)" onmouseover="showTip(event, 'fs34', 194)" class="rt">List</span><span class="pn"><</span><span onmouseout="hideTip(event, 'fs35', 195)" onmouseover="showTip(event, 'fs35', 195)" class="vt">float</span> <span onmouseout="hideTip(event, 'fs36', 196)" onmouseover="showTip(event, 'fs36', 196)" class="rt">array</span><span class="pn">></span><span class="pn">(</span><span class="pn">)</span>
<span class="k">let</span> <span class="k">mutable</span> <span onmouseout="hideTip(event, 'fs47', 197)" onmouseover="showTip(event, 'fs47', 197)" class="mv">h</span> <span class="o">=</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs28', 198)" onmouseover="showTip(event, 'fs28', 198)" class="id">b</span><span class="o">-</span><span onmouseout="hideTip(event, 'fs27', 199)" onmouseover="showTip(event, 'fs27', 199)" class="id">a</span><span class="pn">)</span><span class="o">*</span><span class="n">0.5</span>
<span onmouseout="hideTip(event, 'fs46', 200)" onmouseover="showTip(event, 'fs46', 200)" class="fn">richardsonRows</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs48', 201)" onmouseover="showTip(event, 'fs48', 201)" class="id">Add</span> <span class="pn">(</span><span class="pn">[|</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs23', 202)" onmouseover="showTip(event, 'fs23', 202)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs27', 203)" onmouseover="showTip(event, 'fs27', 203)" class="id">a</span><span class="o">+</span><span onmouseout="hideTip(event, 'fs23', 204)" onmouseover="showTip(event, 'fs23', 204)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs28', 205)" onmouseover="showTip(event, 'fs28', 205)" class="id">b</span><span class="pn">)</span><span class="o">*</span><span onmouseout="hideTip(event, 'fs47', 206)" onmouseover="showTip(event, 'fs47', 206)" class="mv">h</span><span class="pn">|]</span><span class="pn">)</span>
<span class="k">let</span> <span class="k">rec</span> <span onmouseout="hideTip(event, 'fs49', 207)" onmouseover="showTip(event, 'fs49', 207)" class="fn">run</span> <span class="pn">(</span><span class="pn">)</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs50', 208)" onmouseover="showTip(event, 'fs50', 208)" class="id">lastRow</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs29', 209)" onmouseover="showTip(event, 'fs29', 209)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs51', 210)" onmouseover="showTip(event, 'fs51', 210)" class="id">last</span> <span onmouseout="hideTip(event, 'fs46', 211)" onmouseover="showTip(event, 'fs46', 211)" class="id">richardsonRows</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs52', 212)" onmouseover="showTip(event, 'fs52', 212)" class="id">row</span> <span class="o">=</span> <span onmouseout="hideTip(event, 'fs53', 213)" onmouseover="showTip(event, 'fs53', 213)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs54', 214)" onmouseover="showTip(event, 'fs54', 214)" class="id">zeroCreate</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs53', 215)" onmouseover="showTip(event, 'fs53', 215)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs55', 216)" onmouseover="showTip(event, 'fs55', 216)" class="id">length</span> <span onmouseout="hideTip(event, 'fs50', 217)" onmouseover="showTip(event, 'fs50', 217)" class="id">lastRow</span><span class="o">+</span><span class="n">1</span><span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs52', 218)" onmouseover="showTip(event, 'fs52', 218)" class="id">row</span><span class="m">.</span><span class="pn">[</span><span class="n">0</span><span class="pn">]</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs59', 219)" onmouseover="showTip(event, 'fs59', 219)" class="fn">integralEstimateIterative</span> <span onmouseout="hideTip(event, 'fs23', 220)" onmouseover="showTip(event, 'fs23', 220)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs27', 221)" onmouseover="showTip(event, 'fs27', 221)" class="id">a</span> <span onmouseout="hideTip(event, 'fs28', 222)" onmouseover="showTip(event, 'fs28', 222)" class="id">b</span> <span onmouseout="hideTip(event, 'fs50', 223)" onmouseover="showTip(event, 'fs50', 223)" class="id">lastRow</span><span class="m">.</span><span class="pn">[</span><span class="n">0</span><span class="pn">]</span> <span onmouseout="hideTip(event, 'fs47', 224)" onmouseover="showTip(event, 'fs47', 224)" class="mv">h</span>
<span class="k">let</span> <span class="k">mutable</span> <span onmouseout="hideTip(event, 'fs56', 225)" onmouseover="showTip(event, 'fs56', 225)" class="mv">pow4</span> <span class="o">=</span> <span class="n">4.0</span>
<span class="k">for</span> <span onmouseout="hideTip(event, 'fs57', 226)" onmouseover="showTip(event, 'fs57', 226)" class="id">i</span> <span class="o">=</span> <span class="n">0</span> <span class="k">to</span> <span onmouseout="hideTip(event, 'fs53', 227)" onmouseover="showTip(event, 'fs53', 227)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs55', 228)" onmouseover="showTip(event, 'fs55', 228)" class="id">length</span> <span onmouseout="hideTip(event, 'fs50', 229)" onmouseover="showTip(event, 'fs50', 229)" class="id">lastRow</span><span class="o">-</span><span class="n">1</span> <span class="k">do</span>
<span onmouseout="hideTip(event, 'fs52', 230)" onmouseover="showTip(event, 'fs52', 230)" class="id">row</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs57', 231)" onmouseover="showTip(event, 'fs57', 231)" class="id">i</span><span class="o">+</span><span class="n">1</span><span class="pn">]</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs40', 232)" onmouseover="showTip(event, 'fs40', 232)" class="fn">richardsonFormula</span> <span onmouseout="hideTip(event, 'fs52', 233)" onmouseover="showTip(event, 'fs52', 233)" class="id">row</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs57', 234)" onmouseover="showTip(event, 'fs57', 234)" class="id">i</span><span class="pn">]</span> <span onmouseout="hideTip(event, 'fs50', 235)" onmouseover="showTip(event, 'fs50', 235)" class="id">lastRow</span><span class="m">.</span><span class="pn">[</span><span onmouseout="hideTip(event, 'fs57', 236)" onmouseover="showTip(event, 'fs57', 236)" class="id">i</span><span class="pn">]</span> <span onmouseout="hideTip(event, 'fs56', 237)" onmouseover="showTip(event, 'fs56', 237)" class="mv">pow4</span>
<span onmouseout="hideTip(event, 'fs56', 238)" onmouseover="showTip(event, 'fs56', 238)" class="mv">pow4</span> <span class="k"><-</span> <span onmouseout="hideTip(event, 'fs56', 239)" onmouseover="showTip(event, 'fs56', 239)" class="mv">pow4</span><span class="o">*</span><span class="n">4.0</span>
<span class="k">if</span> <span onmouseout="hideTip(event, 'fs31', 240)" onmouseover="showTip(event, 'fs31', 240)" class="fn">stoppingCriteriaNonFunctional</span> <span onmouseout="hideTip(event, 'fs32', 241)" onmouseover="showTip(event, 'fs32', 241)" class="id">tol</span> <span onmouseout="hideTip(event, 'fs46', 242)" onmouseover="showTip(event, 'fs46', 242)" class="id">richardsonRows</span> <span class="k">then</span> <span onmouseout="hideTip(event, 'fs53', 243)" onmouseover="showTip(event, 'fs53', 243)" class="m">Array</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs58', 244)" onmouseover="showTip(event, 'fs58', 244)" class="id">last</span> <span onmouseout="hideTip(event, 'fs50', 245)" onmouseover="showTip(event, 'fs50', 245)" class="id">lastRow</span>
<span class="k">else</span>
<span onmouseout="hideTip(event, 'fs46', 246)" onmouseover="showTip(event, 'fs46', 246)" class="fn">richardsonRows</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs48', 247)" onmouseover="showTip(event, 'fs48', 247)" class="id">Add</span> <span onmouseout="hideTip(event, 'fs52', 248)" onmouseover="showTip(event, 'fs52', 248)" class="id">row</span>
<span onmouseout="hideTip(event, 'fs47', 249)" onmouseover="showTip(event, 'fs47', 249)" class="mv">h</span><span class="k"><-</span><span onmouseout="hideTip(event, 'fs47', 250)" onmouseover="showTip(event, 'fs47', 250)" class="mv">h</span><span class="o">*</span><span class="n">0.5</span>
<span onmouseout="hideTip(event, 'fs49', 251)" onmouseover="showTip(event, 'fs49', 251)" class="fn">run</span><span class="pn">(</span><span class="pn">)</span>
<span onmouseout="hideTip(event, 'fs49', 252)" onmouseover="showTip(event, 'fs49', 252)" class="fn">run</span><span class="pn">(</span><span class="pn">)</span>
</code></pre>
<h2><a name="The-refactor" class="anchor" href="#The-refactor">The refactor</a></h2>
<p>There is a lot of duplicate code in the functions above.</p>
<p>The object-oriented solution to this is the <a href="https://en.wikipedia.org/wiki/Template_method_pattern">Template Method</a> design pattern.
The downside of this approach is that it results in a lot of boiler-plate code with state being shared across multiple classes.</p>
<p>Leif Battermann has a very good <a href="http://blog.leifbattermann.de/2016/03/06/template-method-pattern-there-might-be-a-better-way/">post</a>
on how this can be solved in a functional way using higher-order functions. This results in much more modular and testable code.</p>
<p>Unfortunately, in this case higher-order functions alone will not solve the problem.
The integral estimate needs the previous estimate for its calculation.
This difference in state means the higher-order function would need different signatures for the derivative and integral.</p>
<p>The solution can be found in the excellent paper <a href="http://www.cse.chalmers.se/~rjmh/Papers/whyfp.pdf">Why Functional Programming Matters</a> by John Hughes.
Lazy evaluation is a functional language feature that can greatly improve modularity.</p>
<p>Lazy evaluation allows us to cleanly split the implementation into three parts:</p>
<ol>
<li>A function that produces an infinite sequence of function estimates.</li>
<li>A function that produces a sequence of Richardson estimates from a sequence of function estimates.</li>
<li>A function that iterates a sequence of Richardson estimates and stops at a desired accuracy.</li>
</ol>
<p>Lazy evaluation can be achieved in F# by using the <code>Seq</code> collection and also the <code>lazy</code> keyword.</p>
<h2><a name="Final-code" class="anchor" href="#Final-code">Final code</a></h2>
<pre class="fssnip highlighted"><code lang="fsharp"><span class="c">/// Infinite sequence of derivative estimates.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs62', 253)" onmouseover="showTip(event, 'fs62', 253)" class="fn">derivativeEstimates</span> <span onmouseout="hideTip(event, 'fs23', 254)" onmouseover="showTip(event, 'fs23', 254)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs24', 255)" onmouseover="showTip(event, 'fs24', 255)" class="id">x</span> <span onmouseout="hideTip(event, 'fs45', 256)" onmouseover="showTip(event, 'fs45', 256)" class="id">h0</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs29', 257)" onmouseover="showTip(event, 'fs29', 257)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs6', 258)" onmouseover="showTip(event, 'fs6', 258)" class="id">unfoldInf</span> <span class="pn">(</span><span class="o">(*)</span><span class="n">0.5</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs45', 259)" onmouseover="showTip(event, 'fs45', 259)" class="id">h0</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs29', 260)" onmouseover="showTip(event, 'fs29', 260)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs63', 261)" onmouseover="showTip(event, 'fs63', 261)" class="id">map</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs22', 262)" onmouseover="showTip(event, 'fs22', 262)" class="fn">derivativeEstimate</span> <span onmouseout="hideTip(event, 'fs23', 263)" onmouseover="showTip(event, 'fs23', 263)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs24', 264)" onmouseover="showTip(event, 'fs24', 264)" class="id">x</span><span class="pn">)</span>
<span class="c">/// Infinite sequence of integral estimates.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs64', 265)" onmouseover="showTip(event, 'fs64', 265)" class="fn">integralEstimates</span> <span onmouseout="hideTip(event, 'fs23', 266)" onmouseover="showTip(event, 'fs23', 266)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs27', 267)" onmouseover="showTip(event, 'fs27', 267)" class="id">a</span> <span onmouseout="hideTip(event, 'fs28', 268)" onmouseover="showTip(event, 'fs28', 268)" class="id">b</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs45', 269)" onmouseover="showTip(event, 'fs45', 269)" class="id">h0</span> <span class="o">=</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs28', 270)" onmouseover="showTip(event, 'fs28', 270)" class="id">b</span><span class="o">-</span><span onmouseout="hideTip(event, 'fs27', 271)" onmouseover="showTip(event, 'fs27', 271)" class="id">a</span><span class="pn">)</span><span class="o">*</span><span class="n">0.5</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs65', 272)" onmouseover="showTip(event, 'fs65', 272)" class="id">i0</span> <span class="o">=</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs23', 273)" onmouseover="showTip(event, 'fs23', 273)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs27', 274)" onmouseover="showTip(event, 'fs27', 274)" class="id">a</span><span class="o">+</span><span onmouseout="hideTip(event, 'fs23', 275)" onmouseover="showTip(event, 'fs23', 275)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs28', 276)" onmouseover="showTip(event, 'fs28', 276)" class="id">b</span><span class="pn">)</span><span class="o">*</span><span onmouseout="hideTip(event, 'fs45', 277)" onmouseover="showTip(event, 'fs45', 277)" class="id">h0</span>
<span onmouseout="hideTip(event, 'fs29', 278)" onmouseover="showTip(event, 'fs29', 278)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs6', 279)" onmouseover="showTip(event, 'fs6', 279)" class="id">unfoldInf</span> <span class="pn">(</span><span class="o">(*)</span><span class="n">0.5</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs45', 280)" onmouseover="showTip(event, 'fs45', 280)" class="id">h0</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs29', 281)" onmouseover="showTip(event, 'fs29', 281)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs66', 282)" onmouseover="showTip(event, 'fs66', 282)" class="id">scan</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs59', 283)" onmouseover="showTip(event, 'fs59', 283)" class="fn">integralEstimateIterative</span> <span onmouseout="hideTip(event, 'fs23', 284)" onmouseover="showTip(event, 'fs23', 284)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs27', 285)" onmouseover="showTip(event, 'fs27', 285)" class="id">a</span> <span onmouseout="hideTip(event, 'fs28', 286)" onmouseover="showTip(event, 'fs28', 286)" class="id">b</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs65', 287)" onmouseover="showTip(event, 'fs65', 287)" class="id">i0</span>
<span class="c">/// Richardson extrapolation for a given estimate sequence.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs67', 288)" onmouseover="showTip(event, 'fs67', 288)" class="fn">richardsonExtrapolation</span> <span onmouseout="hideTip(event, 'fs68', 289)" onmouseover="showTip(event, 'fs68', 289)" class="id">s</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs69', 290)" onmouseover="showTip(event, 'fs69', 290)" class="fn">createRow</span> <span onmouseout="hideTip(event, 'fs70', 291)" onmouseover="showTip(event, 'fs70', 291)" class="id">previousRow</span> <span onmouseout="hideTip(event, 'fs71', 292)" onmouseover="showTip(event, 'fs71', 292)" class="id">estimate_i</span> <span class="o">=</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs72', 293)" onmouseover="showTip(event, 'fs72', 293)" class="fn">richardsonAndPow4</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs73', 294)" onmouseover="showTip(event, 'fs73', 294)" class="id">current</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs43', 295)" onmouseover="showTip(event, 'fs43', 295)" class="id">pow4</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs74', 296)" onmouseover="showTip(event, 'fs74', 296)" class="id">previous</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs40', 297)" onmouseover="showTip(event, 'fs40', 297)" class="fn">richardsonFormula</span> <span onmouseout="hideTip(event, 'fs73', 298)" onmouseover="showTip(event, 'fs73', 298)" class="id">current</span> <span onmouseout="hideTip(event, 'fs74', 299)" onmouseover="showTip(event, 'fs74', 299)" class="id">previous</span> <span onmouseout="hideTip(event, 'fs43', 300)" onmouseover="showTip(event, 'fs43', 300)" class="id">pow4</span><span class="pn">,</span> <span onmouseout="hideTip(event, 'fs43', 301)" onmouseover="showTip(event, 'fs43', 301)" class="id">pow4</span><span class="o">*</span><span class="n">4.0</span>
<span onmouseout="hideTip(event, 'fs29', 302)" onmouseover="showTip(event, 'fs29', 302)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs66', 303)" onmouseover="showTip(event, 'fs66', 303)" class="id">scan</span> <span onmouseout="hideTip(event, 'fs72', 304)" onmouseover="showTip(event, 'fs72', 304)" class="fn">richardsonAndPow4</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs71', 305)" onmouseover="showTip(event, 'fs71', 305)" class="id">estimate_i</span><span class="pn">,</span><span class="n">4.0</span><span class="pn">)</span> <span onmouseout="hideTip(event, 'fs70', 306)" onmouseover="showTip(event, 'fs70', 306)" class="id">previousRow</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs29', 307)" onmouseover="showTip(event, 'fs29', 307)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs63', 308)" onmouseover="showTip(event, 'fs63', 308)" class="id">map</span> <span onmouseout="hideTip(event, 'fs75', 309)" onmouseover="showTip(event, 'fs75', 309)" class="fn">fst</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs29', 310)" onmouseover="showTip(event, 'fs29', 310)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs76', 311)" onmouseover="showTip(event, 'fs76', 311)" class="id">cache</span>
<span onmouseout="hideTip(event, 'fs29', 312)" onmouseover="showTip(event, 'fs29', 312)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs66', 313)" onmouseover="showTip(event, 'fs66', 313)" class="id">scan</span> <span onmouseout="hideTip(event, 'fs69', 314)" onmouseover="showTip(event, 'fs69', 314)" class="fn">createRow</span> <span onmouseout="hideTip(event, 'fs29', 315)" onmouseover="showTip(event, 'fs29', 315)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs77', 316)" onmouseover="showTip(event, 'fs77', 316)" class="id">empty</span> <span onmouseout="hideTip(event, 'fs68', 317)" onmouseover="showTip(event, 'fs68', 317)" class="id">s</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs29', 318)" onmouseover="showTip(event, 'fs29', 318)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs78', 319)" onmouseover="showTip(event, 'fs78', 319)" class="id">tail</span>
<span class="c">/// Stopping criteria for a given accuracy and sequence of Richardson estimates.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs79', 320)" onmouseover="showTip(event, 'fs79', 320)" class="fn">stoppingCriteria</span> <span onmouseout="hideTip(event, 'fs32', 321)" onmouseover="showTip(event, 'fs32', 321)" class="id">tol</span> <span onmouseout="hideTip(event, 'fs80', 322)" onmouseover="showTip(event, 'fs80', 322)" class="id">s</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs29', 323)" onmouseover="showTip(event, 'fs29', 323)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs63', 324)" onmouseover="showTip(event, 'fs63', 324)" class="id">map</span> <span onmouseout="hideTip(event, 'fs29', 325)" onmouseover="showTip(event, 'fs29', 325)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs51', 326)" onmouseover="showTip(event, 'fs51', 326)" class="id">last</span> <span onmouseout="hideTip(event, 'fs80', 327)" onmouseover="showTip(event, 'fs80', 327)" class="id">s</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs29', 328)" onmouseover="showTip(event, 'fs29', 328)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs10', 329)" onmouseover="showTip(event, 'fs10', 329)" class="id">triplewise</span>
<span class="o">|></span> <span onmouseout="hideTip(event, 'fs29', 330)" onmouseover="showTip(event, 'fs29', 330)" class="m">Seq</span><span class="pn">.</span><span onmouseout="hideTip(event, 'fs81', 331)" onmouseover="showTip(event, 'fs81', 331)" class="id">find</span> <span class="pn">(</span><span class="k">fun</span> <span class="pn">(</span><span onmouseout="hideTip(event, 'fs27', 332)" onmouseover="showTip(event, 'fs27', 332)" class="id">a</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs28', 333)" onmouseover="showTip(event, 'fs28', 333)" class="id">b</span><span class="pn">,</span><span onmouseout="hideTip(event, 'fs82', 334)" onmouseover="showTip(event, 'fs82', 334)" class="id">c</span><span class="pn">)</span> <span class="k">-></span> <span onmouseout="hideTip(event, 'fs39', 335)" onmouseover="showTip(event, 'fs39', 335)" class="fn">abs</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs27', 336)" onmouseover="showTip(event, 'fs27', 336)" class="id">a</span><span class="o">-</span><span onmouseout="hideTip(event, 'fs28', 337)" onmouseover="showTip(event, 'fs28', 337)" class="id">b</span><span class="pn">)</span><span class="o"><=</span><span onmouseout="hideTip(event, 'fs32', 338)" onmouseover="showTip(event, 'fs32', 338)" class="id">tol</span> <span class="o">&&</span> <span onmouseout="hideTip(event, 'fs39', 339)" onmouseover="showTip(event, 'fs39', 339)" class="fn">abs</span><span class="pn">(</span><span onmouseout="hideTip(event, 'fs28', 340)" onmouseover="showTip(event, 'fs28', 340)" class="id">b</span><span class="o">-</span><span onmouseout="hideTip(event, 'fs82', 341)" onmouseover="showTip(event, 'fs82', 341)" class="id">c</span><span class="pn">)</span><span class="o"><=</span><span onmouseout="hideTip(event, 'fs32', 342)" onmouseover="showTip(event, 'fs32', 342)" class="id">tol</span><span class="pn">)</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs20', 343)" onmouseover="showTip(event, 'fs20', 343)" class="fn">trd</span>
<span class="c">/// Derivative accurate to tol using Richardson extrapolation.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs83', 344)" onmouseover="showTip(event, 'fs83', 344)" class="fn">derivative</span> <span onmouseout="hideTip(event, 'fs32', 345)" onmouseover="showTip(event, 'fs32', 345)" class="id">tol</span> <span onmouseout="hideTip(event, 'fs23', 346)" onmouseover="showTip(event, 'fs23', 346)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs24', 347)" onmouseover="showTip(event, 'fs24', 347)" class="id">x</span> <span onmouseout="hideTip(event, 'fs45', 348)" onmouseover="showTip(event, 'fs45', 348)" class="id">h0</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs62', 349)" onmouseover="showTip(event, 'fs62', 349)" class="fn">derivativeEstimates</span> <span onmouseout="hideTip(event, 'fs23', 350)" onmouseover="showTip(event, 'fs23', 350)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs24', 351)" onmouseover="showTip(event, 'fs24', 351)" class="id">x</span> <span onmouseout="hideTip(event, 'fs45', 352)" onmouseover="showTip(event, 'fs45', 352)" class="id">h0</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs67', 353)" onmouseover="showTip(event, 'fs67', 353)" class="fn">richardsonExtrapolation</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs79', 354)" onmouseover="showTip(event, 'fs79', 354)" class="fn">stoppingCriteria</span> <span onmouseout="hideTip(event, 'fs32', 355)" onmouseover="showTip(event, 'fs32', 355)" class="id">tol</span>
<span class="c">/// Integral accurate to tol using Richardson extrapolation.</span>
<span class="k">let</span> <span onmouseout="hideTip(event, 'fs84', 356)" onmouseover="showTip(event, 'fs84', 356)" class="fn">integral</span> <span onmouseout="hideTip(event, 'fs32', 357)" onmouseover="showTip(event, 'fs32', 357)" class="id">tol</span> <span onmouseout="hideTip(event, 'fs23', 358)" onmouseover="showTip(event, 'fs23', 358)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs27', 359)" onmouseover="showTip(event, 'fs27', 359)" class="id">a</span> <span onmouseout="hideTip(event, 'fs28', 360)" onmouseover="showTip(event, 'fs28', 360)" class="id">b</span> <span class="o">=</span>
<span onmouseout="hideTip(event, 'fs64', 361)" onmouseover="showTip(event, 'fs64', 361)" class="fn">integralEstimates</span> <span onmouseout="hideTip(event, 'fs23', 362)" onmouseover="showTip(event, 'fs23', 362)" class="fn">f</span> <span onmouseout="hideTip(event, 'fs27', 363)" onmouseover="showTip(event, 'fs27', 363)" class="id">a</span> <span onmouseout="hideTip(event, 'fs28', 364)" onmouseover="showTip(event, 'fs28', 364)" class="id">b</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs67', 365)" onmouseover="showTip(event, 'fs67', 365)" class="fn">richardsonExtrapolation</span> <span class="o">|></span> <span onmouseout="hideTip(event, 'fs79', 366)" onmouseover="showTip(event, 'fs79', 366)" class="fn">stoppingCriteria</span> <span onmouseout="hideTip(event, 'fs32', 367)" onmouseover="showTip(event, 'fs32', 367)" class="id">tol</span>
</code></pre>
<h2><a name="Conclusion" class="anchor" href="#Conclusion">Conclusion</a></h2>
<p>Lazy evaluation makes it possible to modularise software into a producer that constructs a large number of possible answers, and a consumer that chooses the appropriate one.</p>
<p>Without it, either state has to be fully generated upfront or production and consumption have to be done in the same place.</p>
<p>Higher-order functions and lazy evaluation can be applied to all software layers.
The <a href="http://www.cse.chalmers.se/~rjmh/Papers/whyfp.pdf">Why Functional Programming Matters</a> paper has examples of their use in game artificial intelligence and other areas.
In my experience the complexity reduction it produces allows software functionality to be pushed further more easily.</p>
<p>Modularity is the most important concept in software design.
It makes software easier to write, understand, test and reuse.
The features of functional languages enable greater modularity.</p>
<div class="tip" id="fs1">module Main</div>
<div class="tip" id="fs2">namespace System</div>
<div class="tip" id="fs3">namespace System.Collections</div>
<div class="tip" id="fs4">namespace System.Collections.Generic</div>
<div class="tip" id="fs5">module Seq<br /><br />from Microsoft.FSharp.Collections</div>
<div class="tip" id="fs6">val unfoldInf : f:('a -> 'a) -> s:'a -> seq<'a><br /><em><br /><br /> Returns an infinite sequence that contains the elements generated by the given computation.</em></div>
<div class="tip" id="fs7">val f : ('a -> 'a)</div>
<div class="tip" id="fs8">val s : 'a</div>
<div class="tip" id="fs9">Multiple items<br />val seq : sequence:seq<'T> -> seq<'T><br /><br />--------------------<br />type seq<'T> = IEnumerable<'T></div>
<div class="tip" id="fs10">val triplewise : s:seq<'a> -> seq<'a * 'a * 'a><br /><em><br /><br /> Returns a sequence of each element in the input and its two predecessors.</em></div>
<div class="tip" id="fs11">val s : seq<'a></div>
<div class="tip" id="fs12">val e : IEnumerator<'a></div>
<div class="tip" id="fs13">IEnumerable.GetEnumerator() : IEnumerator<'a></div>
<div class="tip" id="fs14">System.Collections.IEnumerator.MoveNext() : bool</div>
<div class="tip" id="fs15">val i : 'a ref</div>
<div class="tip" id="fs16">Multiple items<br />val ref : value:'T -> 'T ref<br /><br />--------------------<br />type 'T ref = Ref<'T></div>
<div class="tip" id="fs17">property IEnumerator.Current: 'a</div>
<div class="tip" id="fs18">val j : 'a ref</div>
<div class="tip" id="fs19">val k : 'a</div>
<div class="tip" id="fs20">val trd : 'a * 'b * i:'c -> 'c<br /><em><br /><br /> Returns the third item from a 3-tuple.</em></div>
<div class="tip" id="fs21">val i : 'c</div>
<div class="tip" id="fs22">val derivativeEstimate : f:(float -> float) -> x:float -> h:float -> float<br /><em><br /><br /> Derivative estimate.</em></div>
<div class="tip" id="fs23">val f : (float -> float)</div>
<div class="tip" id="fs24">val x : float</div>
<div class="tip" id="fs25">val h : float</div>
<div class="tip" id="fs26">val integralEstimate : f:(float -> float) -> a:float -> b:float -> h:float -> float<br /><em><br /><br /> Integral estimate (h = (b-a)/n where n is an integer).</em></div>
<div class="tip" id="fs27">val a : float</div>
<div class="tip" id="fs28">val b : float</div>
<div class="tip" id="fs29">Multiple items<br />module Seq<br /><br />from Main<br /><br />--------------------<br />module Seq<br /><br />from Microsoft.FSharp.Collections</div>
<div class="tip" id="fs30">val sumBy : projection:('T -> 'U) -> source:seq<'T> -> 'U (requires member ( + ) and member get_Zero)</div>
<div class="tip" id="fs31">val stoppingCriteriaNonFunctional : tol:float -> rows:List<float array> -> bool<br /><em><br /><br /> Stopping criteria for a given accuracy and list of Richardson estimates.</em></div>
<div class="tip" id="fs32">val tol : float</div>
<div class="tip" id="fs33">val rows : List<float array></div>
<div class="tip" id="fs34">Multiple items<br />type List<'T> =<br />  new : unit -> List<'T> + 2 overloads<br />  member Add : item:'T -> unit<br />  member AddRange : collection:IEnumerable<'T> -> unit<br />  member AsReadOnly : unit -> ReadOnlyCollection<'T><br />  member BinarySearch : item:'T -> int + 2 overloads<br />  member Capacity : int with get, set<br />  member Clear : unit -> unit<br />  member Contains : item:'T -> bool<br />  member ConvertAll<'TOutput> : converter:Converter<'T, 'TOutput> -> List<'TOutput><br />  member CopyTo : array:'T[] -> unit + 2 overloads<br />  ...<br />  nested type Enumerator<br /><br />--------------------<br />List() : List<'T><br />List(capacity: int) : List<'T><br />List(collection: IEnumerable<'T>) : List<'T></div>
<div class="tip" id="fs35">Multiple items<br />val float : value:'T -> float (requires member op_Explicit)<br /><br />--------------------<br />type float = System.Double<br /><br />--------------------<br />type float<'Measure> = float</div>
<div class="tip" id="fs36">type 'T array = 'T []</div>
<div class="tip" id="fs37">val c : int</div>
<div class="tip" id="fs38">property List.Count: int</div>
<div class="tip" id="fs39">val abs : value:'T -> 'T (requires member Abs)</div>
<div class="tip" id="fs40">val richardsonFormula : currentRowR:float -> previousRowR:float -> pow4:float -> float<br /><em><br /><br /> The Richardson formula for a function estimate that has even power error terms.</em></div>
<div class="tip" id="fs41">val currentRowR : float</div>
<div class="tip" id="fs42">val previousRowR : float</div>
<div class="tip" id="fs43">val pow4 : float</div>
<div class="tip" id="fs44">val derivativeNonFunctional : tol:float -> h0:float -> f:(float -> float) -> x:float -> float<br /><em><br /><br /> Derivative accurate to tol using Richardson extrapolation.</em></div>
<div class="tip" id="fs45">val h0 : float</div>
<div class="tip" id="fs46">val richardsonRows : List<float array></div>
<div class="tip" id="fs47">val mutable h : float</div>
<div class="tip" id="fs48">List.Add(item: float array) : unit</div>
<div class="tip" id="fs49">val run : (unit -> float)</div>
<div class="tip" id="fs50">val lastRow : float array</div>
<div class="tip" id="fs51">val last : source:seq<'T> -> 'T</div>
<div class="tip" id="fs52">val row : float []</div>
<div class="tip" id="fs53">module Array<br /><br />from Microsoft.FSharp.Collections</div>
<div class="tip" id="fs54">val zeroCreate : count:int -> 'T []</div>
<div class="tip" id="fs55">val length : array:'T [] -> int</div>
<div class="tip" id="fs56">val mutable pow4 : float</div>
<div class="tip" id="fs57">val i : int</div>
<div class="tip" id="fs58">val last : array:'T [] -> 'T</div>
<div class="tip" id="fs59">val integralEstimateIterative : f:(float -> float) -> a:float -> b:float -> previousEstimate:float -> h:float -> float<br /><em><br /><br /> Iterative integral estimate (h is half the value used in the previous estimate).</em></div>
<div class="tip" id="fs60">val previousEstimate : float</div>
<div class="tip" id="fs61">val integralNonFunctional : tol:float -> f:(float -> float) -> a:float -> b:float -> float<br /><em><br /><br /> Integral accurate to tol using Richardson extrapolation.</em></div>
<div class="tip" id="fs62">val derivativeEstimates : f:(float -> float) -> x:float -> h0:float -> seq<float><br /><em><br /><br /> Infinite sequence of derivative estimates.</em></div>
<div class="tip" id="fs63">val map : mapping:('T -> 'U) -> source:seq<'T> -> seq<'U></div>
<div class="tip" id="fs64">val integralEstimates : f:(float -> float) -> a:float -> b:float -> seq<float><br /><em><br /><br /> Infinite sequence of integral estimates.</em></div>
<div class="tip" id="fs65">val i0 : float</div>
<div class="tip" id="fs66">val scan : folder:('State -> 'T -> 'State) -> state:'State -> source:seq<'T> -> seq<'State></div>
<div class="tip" id="fs67">val richardsonExtrapolation : s:seq<float> -> seq<seq<float>><br /><em><br /><br /> Richardson extrapolation for a given estimate sequence.</em></div>
<div class="tip" id="fs68">val s : seq<float></div>
<div class="tip" id="fs69">val createRow : (seq<float> -> float -> seq<float>)</div>
<div class="tip" id="fs70">val previousRow : seq<float></div>
<div class="tip" id="fs71">val estimate_i : float</div>
<div class="tip" id="fs72">val richardsonAndPow4 : (float * float -> float -> float * float)</div>
<div class="tip" id="fs73">val current : float</div>
<div class="tip" id="fs74">val previous : float</div>
<div class="tip" id="fs75">val fst : tuple:('T1 * 'T2) -> 'T1</div>
<div class="tip" id="fs76">val cache : source:seq<'T> -> seq<'T></div>
<div class="tip" id="fs77">val empty<'T> : seq<'T></div>
<div class="tip" id="fs78">val tail : source:seq<'T> -> seq<'T></div>
<div class="tip" id="fs79">val stoppingCriteria : tol:float -> s:seq<#seq<float>> -> float<br /><em><br /><br /> Stopping criteria for a given accuracy and sequence of Richardson estimates.</em></div>
<div class="tip" id="fs80">val s : seq<#seq<float>></div>
<div class="tip" id="fs81">val find : predicate:('T -> bool) -> source:seq<'T> -> 'T</div>
<div class="tip" id="fs82">val c : float</div>
<div class="tip" id="fs83">val derivative : tol:float -> f:(float -> float) -> x:float -> h0:float -> float<br /><em><br /><br /> Derivative accurate to tol using Richardson extrapolation.</em></div>
<div class="tip" id="fs84">val integral : tol:float -> f:(float -> float) -> a:float -> b:float -> float<br /><em><br /><br /> Integral accurate to tol using Richardson extrapolation.</em></div>