Skip to content

Commit f4b9a8e

Browse files
committed
Prepare article for publication
1 parent 64f55cc commit f4b9a8e

2 files changed

+17
-9
lines changed

_posts/2025-01-06-encapsulating-rod-cutting.html

+8
Original file line numberDiff line numberDiff line change
@@ -419,4 +419,12 @@ <h3 id="c009b6e42470466c9556f52a7c5af175">
419419
<p>
420420
Static types gives you a consistent vocabulary you can use to communicate an API's contract to client developers. What must client code do in order to make a valid method or function call? What guarantees can client code rely on? <a href="/encapsulation-and-solid">Encapsulation</a>, in other words.
421421
</p>
422+
<ins datetime="2025-01-20">
423+
<p>
424+
<strong>P.S. 2025-01-20:</strong>
425+
</p>
426+
<p>
427+
For a type-level technique for modelling the relationship between rod size and price list, see <a href="/2025/01/20/modelling-data-relationships-with-f-types">Modelling data relationships with F# types</a>.
428+
</p>
429+
</ins>
422430
</div>

_posts/2025-01-10-modelling-data-relationships-with-f-types.html _posts/2025-01-20-modelling-data-relationships-with-f-types.html

+9-9
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
layout: post
33
title: "Modelling data relationships with F# types"
44
description: "An F# example implementation of Ghosts of Departed Proofs."
5-
date: 2025-01-10 6:53 UTC
5+
date: 2025-01-20 7:24 UTC
66
tags: [F#, Functional Programming]
77
image: "/content/binary/library-with-computation-context.png"
88
image_alt: "A box labelled 'library' with a 'sandbox' area inside. To its left, another box labelled 'Client code' with an arrow to the library box, as well as an arrow to a box inside the sandbox area labelled 'Client computation'."
@@ -66,7 +66,7 @@ <h3 id="c43d4e8c8e414b46bf65817e7b00e85e">
6666
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:green;">//&nbsp;The&nbsp;rest&nbsp;of&nbsp;the&nbsp;function&nbsp;body...</span></pre>
6767
</p>
6868
<p>
69-
To be clear, in most code bases, this is exactly what I would do. What follows is rather exotic, and hardly suitable for most use cases.
69+
To be clear, in most code bases, this is exactly what I would do. What follows is rather exotic, and hardly suitable for all use cases.
7070
</p>
7171
<h3 id="9457df27904b4b9ea65b4713dea20fbd">
7272
Proofs as values <a href="#9457df27904b4b9ea65b4713dea20fbd">#</a>
@@ -96,7 +96,7 @@ <h3 id="46bc8e47a37f43b3b126946ac29239b0">
9696
The overall idea should look familiar to practitioners of statically-typed functional programming. Instead of plain functions and data structures, we introduce a special 'context' in which we have to run our computations. This is similar to how <a href="/2023/01/09/the-io-monad">the IO monad</a> works, or, in fact, most monads. You're not supposed to <a href="/2019/02/04/how-to-get-the-value-out-of-the-monad">get the value out of the monad</a>. Rather, you should inject the desired behaviour <em>into</em> the monad.
9797
</p>
9898
<p>
99-
We find a similar design with existential types, or with the <a href="https://hackage.haskell.org/package/base/docs/Control-Monad-ST.html">ST monad</a>, on which the ideas in the GDP paper are acknowledged to be based. We even see a mutation-based variation in the article <a href="/2024/06/24/a-mutable-priority-collection">A mutable priority collection</a> where we may consider the <code>Edit</code> API a variation of the ST monad, since it allows 'localized' state mutation.
99+
We find a similar design with existential types, or with the <a href="https://hackage.haskell.org/package/base/docs/Control-Monad-ST.html">ST monad</a>, on which the ideas in the GDP paper are acknowledged to be based. We even see a mutation-based variation in the article <a href="/2024/06/24/a-mutable-priority-collection">A mutable priority collection</a>, where we may think of the <code>Edit</code> API as a variation of the ST monad, since it allows 'localized' state mutation.
100100
</p>
101101
<p>
102102
I'll attempt to illustrate it like this:
@@ -161,10 +161,10 @@ <h3 id="28af8638928c4327b0c3a43d6c36711f">
161161
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color:blue;">else</span>&nbsp;<span style="color:#2b91af;">None</span></pre>
162162
</p>
163163
<p>
164-
The <code>trySize</code> member function issues a <code><span style="color:#2b91af;">Some</span> <span style="color:#2b91af;">Size</span>&lt;<span style="color:#2b91af;">&#39;a</span>&gt;</code> if the <code>candidate</code> is within the size of the price array. As discussed above, we can't completely avoid a run-time check, but now that we have the proof, we don't need to <em>repeat</em> that run-time check if we wanted to use a particular <code>Size</code> value with the same <code>PriceList</code>.
164+
The <code>trySize</code> member function issues a <code><span style="color:#2b91af;">Some</span> <span style="color:#2b91af;">Size</span>&lt;<span style="color:#2b91af;">&#39;a</span>&gt;</code> value if the <code>candidate</code> is within the size of the price array. As discussed above, we can't completely avoid a run-time check, but now that we have the proof, we don't need to <em>repeat</em> that run-time check if we wanted to use a particular <code>Size</code> value with the same <code>PriceList</code>.
165165
</p>
166166
<p>
167-
Notice how immutability is an essential part of this design. If, in the object-oriented manner, we allow a price list to change, we could make it shorter. This could invalidate some proof that we previously issued. Since, however, the price list is immutable, we can trust that once we've checked a size once, it remains valid. You can also think of this as a sort of <a href="/encapsulation-and-solid">encapsulation</a>, in the sense that once we've assured ourselves that an object, or here rather a value, is valid, it remains valid. Indeed, <a href="/2024/06/12/simpler-encapsulation-with-immutability">encapsulation is simpler with immutability</a>.
167+
Notice how immutability is an essential part of this design. If, in the object-oriented manner, we allow a price list to change, we could make it shorter. This could invalidate some proof that we previously issued. Since, however, the price list is immutable, we can trust that once we've checked a size, it remains valid. You can also think of this as a sort of <a href="/encapsulation-and-solid">encapsulation</a>, in the sense that once we've assured ourselves that an object, or here rather a value, is valid, it remains valid. Indeed, <a href="/2024/06/12/simpler-encapsulation-with-immutability">encapsulation is simpler with immutability</a>.
168168
</p>
169169
<p>
170170
You probably still have some questions. For instance, how do we ensure that a size proof issued by one price list can't be used against another price list? Imagine that you have two price lists. One has ten prices, the other twenty. You could have the larger one issue a proof that <em>size 17</em> is valid. What prevents you from using that proof with the smaller price list?
@@ -222,7 +222,7 @@ <h3 id="193350b5b4ab4327b021ac42403dab11">
222222
You may consider the <code>cut</code> function a 'secondary' issuer of <code><span style="color:#2b91af;">Size</span>&lt;<span style="color:#2b91af;">&#39;a</span>&gt;</code> proofs, since it returns such values. If you wanted to call <code>cut</code> again with one of those values, you could.
223223
</p>
224224
<p>
225-
Compared to the previous article, I don't think I changed much else in the <code>cut</code> function than the initial function declaration, and the last line of code, but for good measure, here's the entire function:
225+
Compared to the previous article, I don't think I changed much else in the <code>cut</code> function, besides the initial function declaration, and the last line of code, but for good measure, here's the entire function:
226226
</p>
227227
<p>
228228
<pre><span style="color:blue;">let</span>&nbsp;<span style="color:#74531f;">cut</span>&nbsp;(<span style="color:#2b91af;">PriceList</span>&nbsp;<span style="font-weight:bold;color:#1f377f;">prices</span>&nbsp;:&nbsp;<span style="color:#2b91af;">PriceList</span>&lt;<span style="color:#2b91af;">&#39;a</span>&gt;)&nbsp;(<span style="color:#2b91af;">Size</span>&nbsp;<span style="font-weight:bold;color:#1f377f;">n</span>&nbsp;:&nbsp;<span style="color:#2b91af;">Size</span>&lt;<span style="color:#2b91af;">&#39;a</span>&gt;)&nbsp;:&nbsp;<span style="color:#2b91af;">Cut</span>&lt;<span style="color:#2b91af;">&#39;a</span>&gt;&nbsp;<span style="color:#2b91af;">list</span>&nbsp;=
@@ -249,7 +249,7 @@ <h3 id="193350b5b4ab4327b021ac42403dab11">
249249
The <code>cut</code> function is part of the same module as <code><span style="color:#2b91af;">Size</span>&lt;<span style="color:#2b91af;">&#39;a</span>&gt;</code>, so even though the constructor is <code>private</code>, the <code>cut</code> function can still use it.
250250
</p>
251251
<p>
252-
Thus, the entire proof mechanism is for external use. Internally, the library code may take shortcuts, so it's up to the library author to convince him or herself that the contract holds. In this case, I'm quite confident that the function only issues valid proofs. After all, I've lifted the algorithm from <a href="/ref/clrs">an acclaimed text book</a>, and this particular implementation is covered by more than 10,000 test cases.
252+
Thus, the entire proof mechanism is for external use. Internally, the library code may take shortcuts, so it's up to the library author to convince him- or herself that the contract holds. In this case, I'm quite confident that the function only issues valid proofs. After all, I've lifted the algorithm from <a href="/ref/clrs">an acclaimed text book</a>, and this particular implementation is covered by more than 10,000 test cases.
253253
</p>
254254
<h3 id="dcbcbee557a54a8aaa701484ad89e90f">
255255
Proof-based solve API <a href="#dcbcbee557a54a8aaa701484ad89e90f">#</a>
@@ -332,7 +332,7 @@ <h3 id="2216067c41c44ade8855e4c6f8216d6d">
332332
<span style="color:blue;">let</span>&nbsp;<span style="color:#74531f;">runPrices</span>&nbsp;<span style="font-weight:bold;color:#1f377f;">pl</span>&nbsp;(<span style="font-weight:bold;color:#1f377f;">ctx</span>&nbsp;:&nbsp;<span style="color:#2b91af;">PriceListRunner</span>&lt;<span style="color:#2b91af;">&#39;r</span>&gt;)&nbsp;=&nbsp;<span style="font-weight:bold;color:#1f377f;">ctx</span>.<span style="font-weight:bold;color:#74531f;">Run</span>&nbsp;(<span style="color:#2b91af;">PriceList</span>&nbsp;<span style="font-weight:bold;color:#1f377f;">pl</span>)</pre>
333333
</p>
334334
<p>
335-
As the paper describes, the GDP trick hinges on rank-2 polymorphism, and the only way (that I know of) this is supported in F# is on methods. An object is therefore required, and we define the abstract <code><span style="color:#2b91af;">PriceListRunner</span>&lt;<span style="color:#2b91af;">&#39;r</span>&gt;</code> class for the purpose.
335+
As the paper describes, the GDP trick hinges on rank-2 polymorphism, and the only way (that I know of) this is supported in F# is on methods. An object is therefore required, and we define the abstract <code><span style="color:#2b91af;">PriceListRunner</span>&lt;<span style="color:#2b91af;">&#39;r</span>&gt;</code> class for that purpose.
336336
</p>
337337
<p>
338338
Client code must implement the abstract class to call the <code>runPrices</code> function. Fortunately, since F# has <a href="https://learn.microsoft.com/dotnet/fsharp/language-reference/object-expressions">object expressions</a>, client code might look like this:
@@ -440,7 +440,7 @@ <h3 id="d143cd141fc143c49e14d8e600492dc0">
440440
F# only supports rank-2 polymorphism in method definitions, which makes consuming a GDP API more awkward than in Haskell. The need to create a new type, and the few lines of boilerplate that entails, is a drawback.
441441
</p>
442442
<p>
443-
Even so, the GDP trick is a nice addition to your functional tool belt. You'l hardly need it every day, but I personally like having some specialized tools lying around together with the everyday ones.
443+
Even so, the GDP trick is a nice addition to your functional tool belt. You'll hardly need it every day, but I personally like having some specialized tools lying around together with the everyday ones.
444444
</p>
445445
<p>
446446
But wait! The reason that F# has support for rank-2 polymorphism through object methods is because C# has that language feature. This must mean that the GDP technique works in C# as well, doesn't it? Indeed it does.

0 commit comments

Comments
 (0)