<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: O.F.K.</title>
    <description>The latest articles on DEV Community by O.F.K. (@omanfk).</description>
    <link>https://dev.to/omanfk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F542830%2Fd7929677-35f3-4383-b010-c17d4f16f6e6.png</url>
      <title>DEV Community: O.F.K.</title>
      <link>https://dev.to/omanfk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/omanfk"/>
    <language>en</language>
    <item>
      <title>The death of a QA profession</title>
      <dc:creator>O.F.K.</dc:creator>
      <pubDate>Thu, 18 Dec 2025 21:26:37 +0000</pubDate>
      <link>https://dev.to/omanfk/the-death-of-a-qa-profession-1l2g</link>
      <guid>https://dev.to/omanfk/the-death-of-a-qa-profession-1l2g</guid>
      <description>&lt;p&gt;If you've been looking for a job as a QA or even automation developer in the last few months, you probably know it yourself: there are two professions that seem to have all but vanished completely from job boards: &lt;strong&gt;junior&lt;/strong&gt; software developers, and quality professionals, both manual and automation developers, of all seniority levels.&lt;br&gt;&lt;br&gt;
(You want to nit-pick? Fine... &lt;strong&gt;Three&lt;/strong&gt; professions. You &lt;strong&gt;are&lt;/strong&gt; right, manual QA testers are not the same as automation developers, as much as FE software developers are the same as BE developers. I stand corrected).&lt;/p&gt;

&lt;p&gt;The root cause of this Houdini act, and we quality professionals always seek the root, as you can imagine, is the same: the meteoric rise in LLMs capabilities (no way I'm calling that "intelligence", artificial or not).&lt;/p&gt;

&lt;p&gt;I mean, historically companies always skimped on quality: "Let the developers test one another's code, it'll be fine"... until, "displeased" customers, let's call them that, start threatening taking their money to the competitor unless the product they've paid for started producing the value promised by sales...&lt;br&gt;&lt;br&gt;
(That is, of course, only true for B2B companies. B2C companies have, and I guess will, treat their &lt;strong&gt;users&lt;/strong&gt; as QA testers. Even &lt;em&gt;paying&lt;/em&gt; customers!)&lt;/p&gt;

&lt;p&gt;But with the ever improving LLMs, even companies that &lt;strong&gt;did&lt;/strong&gt; hire quality professionals,manual and/or automation, do so at reduced rate now.&lt;br&gt;&lt;br&gt;
And, loath as I am to admit it - for valid reasons!&lt;/p&gt;

&lt;p&gt;Sure, as a QA professional when I prompt one of those tools for assistance I do better than one of my colleague's go-to prompt of "test this function" - I know how to describe the system under test, what's more likely to cause issue, what to pay extra attention to, you know, being a &lt;strong&gt;quality&lt;/strong&gt; professional.&lt;/p&gt;

&lt;p&gt;But I have to admit... it has come to that that the LLM &lt;strong&gt;routinely&lt;/strong&gt; suggests cases I didn't think about, and they're not always obscure, hard-to-recognize, no-SANE-user-would-do-THAT, edge cases.&lt;br&gt;&lt;br&gt;
Sometimes they're mundane and simple, and had slipped my mind as I was grand-standing and coming up with cases for the 1-in-1000000 edges.&lt;br&gt;&lt;br&gt;
(Yeah, my test plans are &lt;strong&gt;my&lt;/strong&gt; art, and like every artist, I like my art to be awe striking!)&lt;/p&gt;

&lt;p&gt;And they produce good implementations, too!&lt;br&gt;&lt;br&gt;
Not always perfect, but usually good enough that a &lt;em&gt;senior&lt;/em&gt; developer (remember, there're no more juniors to be found) can tweak and fix with very little effort.&lt;/p&gt;

&lt;p&gt;To top things off, like any non-human entity, LLMs don't take coffee breaks, don't get the cold, don't rest on Saturdays (or at all for that matter)...&lt;br&gt;&lt;br&gt;
And, costly as these tools are getting to be, the tools manufacturers want to turn a profit after all, they're still ten-fold cheaper than even a junior quality professional, let alone senior one.&lt;/p&gt;

&lt;p&gt;I'd ask if that was the optimal move on the account of companies, in the long run.&lt;br&gt;&lt;br&gt;
Discussions about companies refusal to hire junior software developers rage over the internet, with many claiming the short-term savings on manpower today will create a generation of non-senior seniors in 10 years or so: developers who never had the chance to be junior, learn from seniors, make mistakes... and fix them - and yet companies are still cutting back on junior software developers.&lt;br&gt;&lt;br&gt;
With that in mind, and an industry's history-long disdain for software quality, until it starts costing, I'm not optimistic about the future of the quality professional trade as a whole.&lt;/p&gt;

&lt;p&gt;What will be the long-term effects of these two trends that have always been there, but exasperated by the rise of LLMs: lack of junior hiring, and lack of quality hiring, on software that &lt;strong&gt;already&lt;/strong&gt; controls every aspect of our lives? Only time will, eventually, tell, but I'm willing to bet my modest savings that nothing good.&lt;/p&gt;

&lt;p&gt;Will these trends change? Likely not, at least not any time soon.&lt;br&gt;&lt;br&gt;
With LLMs becoming better and subscriptions costing half the salary of a junior developer, and VCs pressuring companies' executives to turn a profit &lt;em&gt;any way possible&lt;/em&gt;, C-levels are finally vindicated: a handful of senior &lt;em&gt;developers&lt;/em&gt; &lt;strong&gt;can&lt;/strong&gt; produce quality software (or at least, not as many obvious, show-stopping issues).&lt;/p&gt;

&lt;p&gt;Personally, I suppose we all &lt;strong&gt;will&lt;/strong&gt; pay the price sometimes in the future, when poor quality software will cause horrific results, but as the known meme goes: "Yes, we ended all humanity as we knew it... but for one bright moment in time, &lt;strong&gt;we&lt;/strong&gt; created a lot of profit for the &lt;strong&gt;shareholders&lt;/strong&gt;".&lt;/p&gt;

</description>
      <category>hiring</category>
      <category>career</category>
    </item>
    <item>
      <title>2000 just called, it can't believe you're STILL using L33tC0d3!</title>
      <dc:creator>O.F.K.</dc:creator>
      <pubDate>Sat, 11 Oct 2025 15:53:41 +0000</pubDate>
      <link>https://dev.to/omanfk/2000-just-called-it-cant-believe-youre-still-using-l33tc0d3-1i24</link>
      <guid>https://dev.to/omanfk/2000-just-called-it-cant-believe-youre-still-using-l33tc0d3-1i24</guid>
      <description>&lt;h2&gt;
  
  
  The path to hell is paved in good intentions
&lt;/h2&gt;

&lt;p&gt;Just had a technical take-home test for a position I've applied for.&lt;br&gt;&lt;br&gt;
I know of many senior folks who, in better days, maybe even today, who knows, refused taking &lt;strong&gt;any&lt;/strong&gt; sort of assignment as part of their candidacy flow.&lt;br&gt;&lt;br&gt;
None whatsoever.&lt;br&gt;&lt;br&gt;
"I'm a senior with 15+ years of experience", they reasoned, "my abilities are proven by my tenure in the industry".&lt;/p&gt;

&lt;p&gt;Personally, I don't think years of tenure are a valid metric of skill, but I always &lt;strong&gt;encouraged&lt;/strong&gt; those folks to stick to their beliefs... it meant folks like myself, a little more modest, a little less vain, had the shot those folks forfeited - a win-win situation. 😁&lt;/p&gt;

&lt;p&gt;But today's test has me questioning the industry.&lt;br&gt;&lt;br&gt;
Questioning the industry's ability to gauge candidates correctly, its ability to evolve, ability to admit its mistakes.&lt;br&gt;&lt;br&gt;
The industry's willingness to learn and better itself.&lt;br&gt;&lt;br&gt;
We do, after all, consider ourselves the industry of meritocracy, of disrupt, of change, of the future.&lt;br&gt;&lt;br&gt;
Well, the prevalence of tests like I had to take today make me doubt that.&lt;/p&gt;

&lt;p&gt;You see, the test I took consisted entirely of LeetCode (often styled “L337C0d3”, and from now on in this post, LC) programming puzzles.&lt;br&gt;&lt;br&gt;
And puzzles they are.&lt;/p&gt;

&lt;p&gt;The thing about LC puzzles, the &lt;strong&gt;empirically proven&lt;/strong&gt; thing, is that they are &lt;strong&gt;worse than useless&lt;/strong&gt; as a measure for a candidate's potential to perform well in the day-to-day production setting of a modern software development/engineering department, in fact producing a noise that pollutes the signal!&lt;/p&gt;

&lt;p&gt;How so?&lt;br&gt;&lt;br&gt;
Candidates can train for LC puzzles.&lt;br&gt;&lt;br&gt;
There are literally countless sources dedicated to showing how to tackle these puzzles, all exploiting the fact that LC puzzles all rely on some clever trick that, once recognized, allows for a swift, efficient, solution.&lt;br&gt;&lt;br&gt;
Once a candidate memorized the finite list of trick, learning to recognize each, and also memorized the “trick buster” implementation, all that’s left is to “parrot” the solution and they’re done.&lt;/p&gt;

&lt;p&gt;Further, LC puzzles are usually self-contained: one function, one optimal implementation.&lt;br&gt;&lt;br&gt;
There’s no software &lt;strong&gt;engineering&lt;/strong&gt; involved in solving them. No tradeoffs (e.g., he “trick buster” dictates what data structures to use). No ambivalent, dodgy spec document (to the contrary, LC puzzles descriptions are some of the best spec-ed documents I ever saw, right down to specifying the types and sizes of the input arguments, the expected outputs, and the system’s memory/storage/computation constraints).&lt;/p&gt;

&lt;p&gt;And, since those are automated systems, scoring a candidate’s solution based on the number of unit tests it passed, public and private, hidden, edge-case, ones - there’s no chance of discussion between candidate and evaluator.&lt;br&gt;&lt;br&gt;
It’s an evaluation by not even LLM, rather a crude pass/fail “bean counter”. The more tests you passed, the better the submitted solution must be.&lt;/p&gt;

&lt;p&gt;And the platforms for running those tests?!&lt;br&gt;&lt;br&gt;
"You must have a webcam turned on and facing &lt;strong&gt;your&lt;/strong&gt; screen for the duration of the test - taking your eyes off your own screen is an automatic disqualification”.&lt;br&gt;&lt;br&gt;
“You must have your browser in "Full screen" mode - taking your browser off of full screen mode will result in an automatic disqualification”.&lt;br&gt;&lt;br&gt;
“You can't copy-paste, this functionality has been disabled. Finding a way to circumvent this will result in an automatic disqualification”.&lt;br&gt;&lt;br&gt;
The list of constraints and limitations goes on and on.&lt;/p&gt;

&lt;p&gt;Any infringement of those constraints is referred to as “cheating”, and in the end report you are scored, among your professional capabilities, on “honesty”.&lt;br&gt;&lt;br&gt;
Now, maybe it’s me, but I find it degrading and demeaing that I’m even considered a potential cheat, and then given an “attaboy” for not.&lt;br&gt;&lt;br&gt;
I mean… going in to a test, a stressful enough situation as it is, knowing the people administering the test suspect you of cheating, a-priory, and it’s on you to prove them wrong - that is, come to think of it, &lt;strong&gt;humiliating&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;I also wonder what do the test administrators think they’re actually testing for? What “signal” do they think these tests produce?&lt;br&gt;&lt;br&gt;
Because I'll tell you what they &lt;strong&gt;do&lt;/strong&gt; test: &lt;strong&gt;not&lt;/strong&gt; my coding abilities, &lt;strong&gt;not&lt;/strong&gt; my software engineering skills.&lt;br&gt;&lt;br&gt;
Nope.&lt;br&gt;&lt;br&gt;
What LC puzzles &lt;strong&gt;actually&lt;/strong&gt; test, especially being denied access to Google and LLMs, and under strict time pressure, is &lt;strong&gt;my memory&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Period!&lt;/p&gt;

&lt;p&gt;Ask any software developer, &lt;strong&gt;especially senior ones&lt;/strong&gt;, to implement a simple, but not trivial, task - they will, every single one of them, turn to the following tools for help, in order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;IntelliSense&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;LLMs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Software development Q&amp;amp;A sites (you know which one).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Official documentation for the language/framework/technology&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Exactly the tools these stupid test platforms deny the candidate - some actually don't have even IntelliSense turned on, a glorified online notepad!&lt;br&gt;&lt;br&gt;
I wish I was joking, but sadly, I'm not.&lt;/p&gt;

&lt;p&gt;The more senior you become the less you memorize specific APIs and commands, instead honing your engineering skills: the trade-offs of different data-structures, tighetning a loop by using collection functions instead of imperative constructs, manging state.&lt;br&gt;&lt;br&gt;
Implementation details are exactly that, details - that’s what all the above mentioned tools are for.&lt;/p&gt;

&lt;p&gt;I don’t care to remember the exact syntax of a “forEach” function, or maybe it’s called “map” in this particular programming language?&lt;br&gt;&lt;br&gt;
I just know I’d be better off using it then implementing an imperative “for loop” by hand, but without access to my tools of the trade I’m forced to use what my pressure blacked-out brains knows by heart - exactly the sub-optimal, error-prone, hand-rolled, imperative loop!&lt;/p&gt;

&lt;p&gt;And by the way... my code &lt;strong&gt;did&lt;/strong&gt; pass &lt;strong&gt;all&lt;/strong&gt; the unit tests!&lt;br&gt;&lt;br&gt;
I mean, sure, I submitted my code “on the buzzer” (each question has a time limit, ‘cause why not pressure the candidate some more?!), and the submitted code is hand-rolled, unmaintainable, imperative, non-idiomatic, sub-optimal performing, using obsolete APIs, that wouldn't pass a real-world’s production-grade code review.&lt;br&gt;&lt;br&gt;
But it &lt;strong&gt;did&lt;/strong&gt; pass &lt;strong&gt;all&lt;/strong&gt; the unit tests, including the private, hidden, ones.&lt;/p&gt;

&lt;p&gt;Guess that means I'm a &lt;strong&gt;great&lt;/strong&gt; software developer and engineer.&lt;br&gt;&lt;br&gt;
I'd be expecting your offer, at premium rates, any day now.&lt;/p&gt;

&lt;h2&gt;
  
  
  My way
&lt;/h2&gt;

&lt;p&gt;"So, mister smarty-pants, if you're soooo smart, how would you have us evaluate candidates?"&lt;br&gt;&lt;br&gt;
Wow, I'm so happy you asked.&lt;br&gt;&lt;br&gt;
A little unexpected, I didn't prepare anything, but let me see what I can come up with on the spot.&lt;/p&gt;

&lt;p&gt;Well, to begin with - ditch those stupid LC puzzles and platforms!&lt;br&gt;&lt;br&gt;
I know, they're &lt;strong&gt;easy&lt;/strong&gt;: you select a bunch of questions from the platform’s literally endless supply, the platform's automated judge gives a pass/fail score to each question, crunches all the numbers for you, and displays the top N percent candidates (possibly also factoring speed of completion - ‘cause there’s nothing suspicious about a candidate completing a perfect solution to a puzzle in under 2 minutes from the clock starting to count. Everything’s Kosher).&lt;/p&gt;

&lt;p&gt;Easy.&lt;br&gt;&lt;br&gt;
Fast.&lt;br&gt;&lt;br&gt;
Convenient.&lt;br&gt;&lt;br&gt;
Requires you to spend no time whatsoever personally getting to know the candidate.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Wrong!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You want to evaluate a candidate's &lt;strong&gt;real&lt;/strong&gt; software engineering skills - ask them to &lt;strong&gt;engineer&lt;/strong&gt; some software.&lt;br&gt;&lt;br&gt;
I know… what a total 🤯!&lt;/p&gt;

&lt;p&gt;Have the candidate implement a &lt;strong&gt;small&lt;/strong&gt; feature you already have running in production for years - no one like doing free work, or even feeling like it!&lt;br&gt;&lt;br&gt;
And I mean small: an optimal solution will have at most 2-3 classes, each with 2-3 methods.&lt;/p&gt;

&lt;p&gt;Already you got a signal about how the candidate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Organizes their code&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What naming convention do they use - robust and legible, or the dreaded &lt;code&gt;int i&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Did they bother adding tests, and if so, were those test high-quality, or basic, low-hanging fruit&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Isn’t this kind of signal already ten-folds better than LC puzzles? Of course it is!&lt;br&gt;&lt;br&gt;
But we’re just getting started!&lt;/p&gt;

&lt;p&gt;Let the candidate take their time... no time constraint, no pressure.&lt;br&gt;&lt;br&gt;
You &lt;strong&gt;continue&lt;/strong&gt; interviewing candidates all the time, up to when one has signed the contract.&lt;br&gt;&lt;br&gt;
If a candidate is too lax, taking their time - that's their problem. The opportunity will slip right through their fingers.&lt;/p&gt;

&lt;p&gt;Also, let the candidate use whatever tools they feel like.&lt;br&gt;&lt;br&gt;
You put it in the assignment spec: "Use whatever tools you want. For all we care, if you can summon the occult to help you with the task - go right ahead, and don't forget to draw a pentagram on the floor first!"&lt;br&gt;&lt;br&gt;
You'd be surprised what a good chuckle where you least expect it will do to a candidate's morale.&lt;/p&gt;

&lt;p&gt;By the way, speaking of "the spec", the assignment's actual text, the one the candidate needs to implement - make it vague on purpose. Omit key details. Leave out acceptance criteria for certain test cases.&lt;br&gt;&lt;br&gt;
You know, like the spec your own PM hands &lt;strong&gt;you&lt;/strong&gt;. Every... single... sprint.&lt;/p&gt;

&lt;p&gt;And, know what? You &lt;strong&gt;don't&lt;/strong&gt; have to be super-responsive either, when, &lt;strong&gt;if&lt;/strong&gt;, the candidate comes a-knocking on your email box’s door asking for clarifications - excellent signal if they do, excellent &lt;strong&gt;negative&lt;/strong&gt; signal if they don't.&lt;br&gt;&lt;br&gt;
Is your PM just waiting at your beck and call? No?! What a shocker.&lt;br&gt;&lt;br&gt;
You don’t have to be at the candidate’s either. Just make sure to get back to them &lt;strong&gt;eventually&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In essence, you extend the candidate the same respect, &lt;strong&gt;and expectations&lt;/strong&gt;, you will once they sign the contract and come onboard your engineering team.&lt;/p&gt;

&lt;p&gt;Once the candidate submitted their work have your LLM of choice review it.&lt;br&gt;&lt;br&gt;
What for, what are the review criteria, you decide - only you know what skills and engineering best practices make your team vibe like the pit crew of a premium F1 championship team.&lt;/p&gt;

&lt;p&gt;Of the candidates who passed the LLM verdict, you have a &lt;strong&gt;long&lt;/strong&gt; discussion, regarding their solution.&lt;br&gt;&lt;br&gt;
This is the time to find out if they &lt;strong&gt;truly&lt;/strong&gt; understand the code they submitted, what made them take the design choices, the data structure compromises - the engineering.&lt;/p&gt;

&lt;p&gt;Again, I can't tell you how to run such a conversation, but experience taught me that when facing another like-minded engineer, the conversation flows of its own accord.&lt;br&gt;&lt;br&gt;
A jittery conversation, especially if the candidate is senior and has been around the block, is in itself an excellent signal. A negative one, but excellent all the same.&lt;/p&gt;

&lt;p&gt;After the round of personal, technical, interviews, the only &lt;strong&gt;real&lt;/strong&gt; hard decision left for you to make is which of the candidates who made a positive impression gets the offer.&lt;br&gt;&lt;br&gt;
Hey, that's why you're getting paid the manager's salary... big bucks for big decisions.&lt;/p&gt;

&lt;p&gt;Yes, this flow &lt;strong&gt;is&lt;/strong&gt; time consuming.&lt;br&gt;&lt;br&gt;
It's &lt;strong&gt;not&lt;/strong&gt; easy.&lt;br&gt;&lt;br&gt;
Nor is it convenient.&lt;br&gt;&lt;br&gt;
It &lt;strong&gt;will&lt;/strong&gt; “eat up” into your already tight schedule (another deadline was moved up a week?! Geez!)&lt;br&gt;&lt;br&gt;
It &lt;strong&gt;will&lt;/strong&gt; require you to interact with the candidates, plural, for an extended period of time (in the process, giving you an insight about their personality, not only their coding skills. How’s about that?!)&lt;br&gt;&lt;br&gt;
It’s definitely &lt;strong&gt;not&lt;/strong&gt; fun.&lt;/p&gt;

&lt;p&gt;But… you need to ask yourself what are you optimizing for when recruiting - your own momentarily leisure, or your team's future success?&lt;br&gt;&lt;br&gt;
Like I said, big bucks for big decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's a cultural thang
&lt;/h2&gt;

&lt;p&gt;As I conclude this post I'd like to say to those companies that in 2025, coming on 2026, still use LC puzzles, on restrictive platforms - don't call me, I won't call you.&lt;/p&gt;

&lt;p&gt;Yes, economy is in the gutter right now, coming by interview opportunities is harder than winning the PowerBall. Still, that's fine, you do your thing, I’ll do mine.&lt;/p&gt;

&lt;p&gt;I'm probably not a good cultural fit for you anyway - I like airing my grievances, not just pretending everything is fine, smiling and waving my hand.&lt;/p&gt;

&lt;p&gt;I speak up when I feel I've been wronged, and I don't mince my words either.&lt;/p&gt;

&lt;p&gt;I also like working with, and for, smart people, people that appreciate advanced, modern, technology, and know how to take advantage of it.&lt;/p&gt;

&lt;p&gt;I don't like dinosaurs, never got the fascination.&lt;/p&gt;

&lt;p&gt;And I especially don't like being judged unfairly, by lazy folks too complacent to step up their own game, and put in the work.&lt;/p&gt;

&lt;p&gt;We’ve already established I &lt;strong&gt;am&lt;/strong&gt; a technical fit - all my solutions to your LC puzzles passed all unit tests.&lt;br&gt;&lt;br&gt;
But culture is as important - and &lt;strong&gt;I&lt;/strong&gt; don’t like yours!&lt;/p&gt;

&lt;p&gt;KTHXBYE&lt;/p&gt;

</description>
      <category>leetcode</category>
      <category>interview</category>
      <category>hiring</category>
      <category>career</category>
    </item>
    <item>
      <title>From CAP to GAP?</title>
      <dc:creator>O.F.K.</dc:creator>
      <pubDate>Fri, 10 Oct 2025 07:02:04 +0000</pubDate>
      <link>https://dev.to/omanfk/from-cap-to-gap-269h</link>
      <guid>https://dev.to/omanfk/from-cap-to-gap-269h</guid>
      <description>&lt;h2&gt;
  
  
  Humble beginnings
&lt;/h2&gt;

&lt;p&gt;Testing a piece of existing code is easy. That is, at least, the common wisdom: you know what the code is supposed to do, you craft inputs to trigger certain, deterministic, behaviors in the code, and assert the output for each is as expected. Like I said - easy.&lt;/p&gt;

&lt;p&gt;Well, sorta...&lt;br&gt;&lt;br&gt;
Even if we discount hardware issues, e.g., network loss in the middle of multi-GB file downloading, or hard drive on logging server becoming full (and who's idea was it to forgo monitoring on &lt;strong&gt;that&lt;/strong&gt; server?!), one needs to account for edge cases such as invalid user input, which the application should handle gracefully, or worse, &lt;strong&gt;seemingly&lt;/strong&gt; valid input - that might cause some really weird behaviors if left unchecked - I'm looking at you "divide by zero", you sneaky bastard.&lt;/p&gt;

&lt;p&gt;So, smart folks, when faced with this, eh, uneasiness, came up with a new, &lt;strong&gt;complimentary&lt;/strong&gt; testing paradigm: property-based testing: you identify the invariant of the piece of code, the &lt;strong&gt;underlying&lt;/strong&gt; behavior, the "what is this code all about, like, deep down", and you let the computer generate inputs, both valid-but-fishy, and outright invalid, to stress that understanding of what the function is really all about.&lt;br&gt;&lt;br&gt;
Easy.&lt;br&gt;&lt;br&gt;
So easy, in fact, that the simple addition function, you know, good ole' &lt;code&gt;+&lt;/code&gt; operator, is... let's say, &lt;a href="https://fsharpforfunandprofit.com/posts/property-based-testing/" rel="noopener noreferrer"&gt;complex&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And do I even dare consider load and stress testing, requiring intimate understanding of the domain, the available resources, and knowing how to analyze the results to make the call if our system has hit a peak, has hit a fail-point, or, ideally, can take even more abuse?&lt;/p&gt;

&lt;p&gt;And don't even get me started on "pentesting", i.e., "Penetration testing", the act of &lt;strong&gt;actively&lt;/strong&gt; looking for security breaches in the application and exploiting them to prove they exist!  &lt;/p&gt;

&lt;p&gt;So, to reiterate, testing a given piece of code is easy, it's what mathematicians call a "solved problem".&lt;br&gt;&lt;br&gt;
After all, we all do it all the time with impeccable implementation and results - it's what we call "unit testing".&lt;/p&gt;

&lt;h2&gt;
  
  
  Artificial intelligence is no match for human stupidity
&lt;/h2&gt;

&lt;p&gt;Stage enter left, LLMs.&lt;br&gt;&lt;br&gt;
I mean... how do we even QA, test, and verify this new Golem we unleashed upon ourselves?!&lt;/p&gt;

&lt;p&gt;But that's a loaded question, isn't it?&lt;br&gt;&lt;br&gt;
If by testing the LLM we mean verifying the code it outputs does what we expect (what a strange goal to have, I know), without non-existing APIs (an affliction I &lt;strong&gt;still&lt;/strong&gt; experience every now and then, especially in less mainstream technologies where training code was, still is, scarce, and the LLM is forced to extrapolate from the rest of its training), that it's idiomatic (for the technology we're using), in a performant and secure way - well, that's back to testing 101, our solved problem.&lt;br&gt;&lt;br&gt;
Easy.&lt;/p&gt;

&lt;p&gt;I'll even see my previous claim and raise - I further claim I can, to a great degree of confidence, verify an LLM's response, in &lt;strong&gt;natural language&lt;/strong&gt;, to questions to which the expected response should follow some pattern and structure within a certain context.&lt;br&gt;&lt;br&gt;
In other words, questions whose answer conforms to some formal specification - but said answer may be expressed in natural language, not necessarily in code.&lt;/p&gt;

&lt;p&gt;For example: say I'm a network engineer trying to partition my current intranet into subnets, each allowing only certain organizational users, and only certain protocols, and I prompt the LLM for help, identifying the issue ("need to partition the intranet to subnet allowing only given users and protocols") and the inputs (i.e., list of users, protocols, and mapping.)&lt;/p&gt;

&lt;p&gt;I now expect the response to include the word "subnets", though I'd also be looking for "groups" or "sets", and &lt;strong&gt;in the context&lt;/strong&gt; of subnets, I expect to, eventually, encounter all the inputs I listed: &lt;strong&gt;all&lt;/strong&gt; the usernames &lt;strong&gt;and&lt;/strong&gt; protocols.&lt;br&gt;&lt;br&gt;
Further, I expect to find some code to actually carry out the network partitioning.&lt;br&gt;&lt;br&gt;
All this should be given in some flow, some context, that is continuous, flows from idea to implementation naturally.&lt;/p&gt;

&lt;p&gt;I now claim that if I find &lt;strong&gt;all&lt;/strong&gt; of the above expected text in the LLM's response I can claim with &lt;strong&gt;high confidence&lt;/strong&gt; its response is correct, but missing even one detail from that list immediately disqualifies the response.&lt;br&gt;&lt;br&gt;
A logical Bloom filter.&lt;/p&gt;

&lt;p&gt;Yes, obviously, the verifier is an LLM itself, but I posit it needs to be a very simple, nerfed, model, not some beast requiring a cluster of GPUs to run, having been trained on Petabytes of &lt;strong&gt;domain-specific&lt;/strong&gt; data.&lt;br&gt;&lt;br&gt;
The verifier's only job would be to sniff out the expected keywords and validate they are in a semantic structure, context, that makes sense.&lt;br&gt;&lt;br&gt;
Again, not straight forward, but definitely doable using today's technology and current models, and can be run against &lt;strong&gt;all&lt;/strong&gt; domains, generally.&lt;/p&gt;

&lt;p&gt;Which brings us to ask how can we verify an LLM's response to open-ended questions, questions whose response is, by definition, not governed by spec?&lt;br&gt;
How do you verify a response that is deliberately fashioned to &lt;strong&gt;mimic human thinking and intelligence&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;I encourage you to think about this question for a couple of seconds: how &lt;strong&gt;do&lt;/strong&gt; we verify an answer by our human colleagues?&lt;br&gt;&lt;br&gt;
That is, after all, what we're trying to achieve here: an application to verify a human-like response, to &lt;strong&gt;any&lt;/strong&gt; question, in &lt;strong&gt;any&lt;/strong&gt; domain.&lt;br&gt;&lt;br&gt;
Can &lt;strong&gt;we&lt;/strong&gt; do it? Probably not, but &lt;strong&gt;why&lt;/strong&gt; not?&lt;/p&gt;

&lt;h2&gt;
  
  
  Fundamentally speaking
&lt;/h2&gt;

&lt;p&gt;The reason we can't, and there's no "probably" about it, no one single person can verify all open-ended responses, to any and all domains, is that in order to be able to validate an answer given by someone else, the verifier has to be two things: as intelligent as the one giving the answer, so as to distinguish a valid answer from ear-pleasing mumbo-jumbo, have as much domain expertise as the person providing the answer.&lt;/p&gt;

&lt;p&gt;When thinking about a so-called "universal LLM verifier" we'd like to impose two more constraints: the system needs to be generalized enough to be able to validate responses in any domain, and it needs to be automated with no human intervention or interaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tasty, low calories, cheap - pick only two
&lt;/h2&gt;

&lt;p&gt;But that, alas, is not possible!&lt;br&gt;&lt;br&gt;
&lt;strong&gt;We've hit the "GAP theorem of LLMs".&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes, yes, I can hear your "the what now?!" gasps all the way here.&lt;/p&gt;

&lt;p&gt;As we all know today, with the advent of distributed systems' engineering, a fundamental limitation of such systems arose: the CAP theorem of distributed system, stating that in such systems there can only ever be two attributes &lt;strong&gt;guaranteed&lt;/strong&gt; by the system's design, out of three possible: Consistency (data is correct across all components of the system at the same time), Atomicity (operations either succeed end-to-end, or, upon failing, leave the system unchanged), and Partition (data can be split across several nodes in the system without degrading system's operation).&lt;br&gt;&lt;br&gt;
Consistency, Atomicity, Partition - choose any two.&lt;/p&gt;

&lt;p&gt;Well, in the emergent realm of LLMs we face a similar problem: GAP - Generality (a verifier's ability to verify responses of any domain), Automation (no human intervention/interaction required), and Precision (verifier's decision is correct, to a &lt;strong&gt;very&lt;/strong&gt; high degree of confidence) (yes, I know... "Accuracy". Precision lends the "P" to make the cool acronym. Humor me,constraint why don'tcha?!)&lt;/p&gt;

&lt;p&gt;And, much like CAP theorem, so is the GAP theorem not solvable by doing the thing we humans love doing when in dire straits: putting more force into the matter.&lt;/p&gt;

&lt;p&gt;Generality isn't solved by adding more nerfed, equivalent, LLM servers. It may cut down the time to reach a consensus whether the text being verified is valid or not, but using more of the same can't, by definition, upgrade the outcome.&lt;/p&gt;

&lt;p&gt;Automation can't be solved by putting more servers in play, period. If a domain is deemed so risky that any and all LLM responses &lt;strong&gt;must&lt;/strong&gt; be verified by a human expert in the domain, than it must.&lt;/p&gt;

&lt;p&gt;Precision, like generality, can also &lt;strong&gt;not&lt;/strong&gt; be mitigated by adding more servers of the same ability: they all have the same training, none is an expert on the domain in question, none can be an authoritative answer.&lt;/p&gt;

&lt;p&gt;Much like CAP theorem is a &lt;strong&gt;fundamental&lt;/strong&gt; limitation on distributed system stemming from current technologies used to implement said systems, so is GAP theorem a &lt;strong&gt;fundamental&lt;/strong&gt; limitation on a "Universal LLM verifying" machine... at least given current LLM technologies.&lt;/p&gt;

&lt;p&gt;I'm willing to concede that if, say, you have an Alibaba-like model, running 405B parameters, probably GAP theorem does &lt;strong&gt;not&lt;/strong&gt; apply to your model, it being general &lt;strong&gt;and&lt;/strong&gt; precise by sheer power of computation... so, yeah, I guess with &lt;strong&gt;enough&lt;/strong&gt; additional power we can overcome GAP theorem.&lt;br&gt;&lt;br&gt;
Then again, considering the costs of running even a straight forward question on that model... I doubt even Alibaba is using their monster model for anything but the most pressing, urgent, profit-driving issues.&lt;br&gt;&lt;br&gt;
So, not as general as it may seem at first.&lt;br&gt;&lt;br&gt;
GAP theorem stands tall.&lt;/p&gt;

&lt;h2&gt;
  
  
  The future's calling
&lt;/h2&gt;

&lt;p&gt;Much like CAP theorem doesn't prevent us from building distributed systems, merely forces us to make compromises in their design, &lt;strong&gt;and be honest about it&lt;/strong&gt;, so will GAP theorem not prevent us from building LLM verifiers... it &lt;strong&gt;does&lt;/strong&gt; mean there can never be a "universal verifier", though, and it forces anyone building such a verifier to make a compromise, and be honest about it, to themselves, and to potential clients!&lt;/p&gt;

&lt;p&gt;Some of these future tools will choose to forgo generality - those will be tools used in high-risk domains, highly regulated, high-stakes, lives on the line domains.&lt;br&gt;&lt;br&gt;
Tools will be built trained on domain-specific data, regulated by human domain experts until the model shows an extreme confidence in the domain.&lt;/p&gt;

&lt;p&gt;Some will choose to compromise the verifier's precision - being "close enough" will be good enough.&lt;br&gt;&lt;br&gt;
I'm guessing those will be the bulk of these tools. We need to make sure our original LLM's answer is plausible-sounding, we're not in the business of splitting hairs. We need time-to-market, not the rigor of a mathematical proof.&lt;/p&gt;

&lt;p&gt;I don't believe any of these tools will forgo automation, though, that would be counter-productive and purpose-defeating.&lt;br&gt;&lt;br&gt;
Well, perhaps some of the high-risks domain mentioned above will be made to give up automation, as well as generality, by regulations. Maybe not.&lt;/p&gt;

&lt;p&gt;Whichever way each of these tools will sway, it will not be able to disguise - and that's a win for the entire industry.&lt;br&gt;&lt;br&gt;
Since a universal verifier is unattainable, each of these tools will have to concede which of the three "legs" of GAP it gave up, allowing its users to better evaluate whether &lt;strong&gt;that&lt;/strong&gt; tool is the right tool for their use-case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Are we there yet?
&lt;/h2&gt;

&lt;p&gt;The realm, and it &lt;strong&gt;is&lt;/strong&gt; a realm, not "just a new technology" by even the slightest chance, of LLMs is in its infancy, and already showing just how vast and grand it will be. And it will.&lt;/p&gt;

&lt;p&gt;As with anything new, uncharted, unknown, nothing is really worked-out yet. Nothing is a "solved problem".&lt;br&gt;&lt;br&gt;
Heck, we don't even know what we don't know about this... thing.&lt;/p&gt;

&lt;p&gt;It's good, therefore, to at least know its limits. To know they actually exist. That, vast as it is, even this realm does have a line in the sand that can't be crossed.&lt;br&gt;&lt;br&gt;
Maybe it's just me, but I find it reassuring. As a father to a toddler, I know all too well how much limits are something every infant needs to have... even one made up of bits. 😃&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>softwareengineering</category>
      <category>testing</category>
    </item>
    <item>
      <title>The function not dispatched</title>
      <dc:creator>O.F.K.</dc:creator>
      <pubDate>Sun, 28 Sep 2025 10:49:07 +0000</pubDate>
      <link>https://dev.to/omanfk/the-function-not-dispatched-548a</link>
      <guid>https://dev.to/omanfk/the-function-not-dispatched-548a</guid>
      <description>&lt;p&gt;Reading the Julia language's documentation, it says that unlike many traditional languages, Julia support multiple dispatching of functions (from here on, &lt;em&gt;MD&lt;/em&gt;), as opposed to traditional OOP languages' single dispatch, or even the more modern function overloading (onwards, &lt;em&gt;FO&lt;/em&gt;.)&lt;/p&gt;

&lt;p&gt;I know what single dispatch is, anyone who ever "dotted into" a method call in an OO language knows what single dispatch is.&lt;br&gt;&lt;br&gt;
I also know what function overloading is (thank you Elixir for that.)&lt;br&gt;&lt;br&gt;
But multiple dispatch?&lt;/p&gt;

&lt;p&gt;So I did some digging and now would like to post about it too, adding my own two cents. 😉&lt;br&gt;
I'll start with single dispatch, for completeness sake.&lt;/p&gt;
&lt;h2&gt;
  
  
  You had one task
&lt;/h2&gt;

&lt;p&gt;As mentioned earlier, if you ever "dotted into" a method call in most dynamic OO languages - congratulations, you executed a single dispatch operation.  &lt;/p&gt;

&lt;p&gt;What single dispatch simply means is that when a function is called, the compiler knows &lt;strong&gt;exactly&lt;/strong&gt; what implementation of the function to call, because there is only ever one such implementation!&lt;br&gt;&lt;br&gt;
Furthermore, even if there are several similarly named functions across the system, even if all those functions take the same number of arguments and the same types for those same arguments, its arguments' &lt;strong&gt;shape&lt;/strong&gt; - the compiler &lt;strong&gt;still&lt;/strong&gt; knows which &lt;strong&gt;unique&lt;/strong&gt; function to call.&lt;/p&gt;

&lt;p&gt;How? Due to the function's first argument - the receiving object, the object we "dot-into".&lt;br&gt;&lt;br&gt;
For example, in Ruby:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; [3, 2, 1]&lt;/span&gt;
&lt;span class="s2"&gt;"Hi"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; "iH"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks like we're calling the same method, &lt;code&gt;reverse&lt;/code&gt;, with the same shape, i.e., no argument at all, on both a string and an array &lt;strong&gt;instances&lt;/strong&gt;, yet Ruby's interpreter is able to discern which implementation to use for each.&lt;br&gt;&lt;br&gt;
This is because the receiver, a string in one case, and array in the other, dictates which implementation would be called!&lt;/p&gt;

&lt;p&gt;More so, since Ruby allows "monkey patching", we could try to "get creative" and do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reverse&lt;/span&gt;
        &lt;span class="s2"&gt;""&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One might now ask, which method is dispatched when &lt;code&gt;"A string".reverse&lt;/code&gt; is called, but the answer is that the last implementation (in terms of code hierarchy, &lt;strong&gt;not&lt;/strong&gt; chronological order) wins, so in this case, our patched version is the one that's called and would return the empty string.  &lt;/p&gt;

&lt;p&gt;There is only ever one possible method implementation for any given method name for a given type!&lt;br&gt;&lt;br&gt;
By "qualifying" the method with its receiver, the instance being invoked, we let the compiler know &lt;strong&gt;exclusively&lt;/strong&gt; which implementation to dispatch.&lt;/p&gt;

&lt;p&gt;Most of the popular dynamic OO languages support &lt;strong&gt;only&lt;/strong&gt; single dispatch - JavaScript (TypeScript allows multiple dispatch, but since TS is eventually compiled down to JS, this is risky and error prone in subtle, hard to catch, ways), both Python and Ruby, as does Go.&lt;/p&gt;

&lt;p&gt;On the other hand, language such as Java, and C#, for example, support a form of function dispatching where the compiler has to decide which implementation is the correct one - function overloading.&lt;/p&gt;
&lt;h2&gt;
  
  
  Two methods diverged in a code
&lt;/h2&gt;

&lt;p&gt;Suppose I'm malcontent with the previous languages' inability to let me define just &lt;strong&gt;one function&lt;/strong&gt;, but one that can take several different arguments' shapes, and return different outputs according to the arguments provided.&lt;br&gt;&lt;br&gt;
Put another way, I want to define a "family" of similarly named functions, differing in their arguments' shapes, and outputs.&lt;/p&gt;

&lt;p&gt;For example, I want to to define a custom type, i.e., a class, say &lt;code&gt;Sum&lt;/code&gt;, with a member method &lt;code&gt;sum&lt;/code&gt; that sums its arguments. I also want the function to operate only on the following arguments' shapes are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two integers&lt;/li&gt;
&lt;li&gt;Two floats&lt;/li&gt;
&lt;li&gt;An integer and a float (or vice-versa, a float and an integer)&lt;/li&gt;
&lt;li&gt;A single integer&lt;/li&gt;
&lt;li&gt;A single float&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Generics (AKA "ad-hoc polymorphism") won't do.&lt;br&gt;&lt;br&gt;
A generic function &lt;code&gt;sum&amp;lt;'T&amp;gt;&lt;/code&gt;, by definition would allow &lt;code&gt;'T&lt;/code&gt; to be &lt;strong&gt;any&lt;/strong&gt; type: a string, a tuple of an arbitrary shape, a custom type, etc.&lt;br&gt;&lt;br&gt;
There &lt;strong&gt;can't&lt;/strong&gt; be a &lt;strong&gt;single&lt;/strong&gt; implementation that supports all possible inputs.&lt;br&gt;&lt;br&gt;
What I want is actually the &lt;strong&gt;diametric opposite&lt;/strong&gt; of a generic function - I want a &lt;strong&gt;specialized&lt;/strong&gt; function that only allows a very specific enumeration of arguments' shapes, allowing the compiler to give me compile-time type safety guarantees!&lt;/p&gt;

&lt;p&gt;You may have also noted that just as I was talking about dynamic languages when talking about single dispatch, with Go being the notable exception, I am now speaking of "compile-time guarantees", since we're talking about function overloading.&lt;br&gt;&lt;br&gt;
That's because you only get FO in compiled languages that support &lt;strong&gt;static typing&lt;/strong&gt; (not necessarily strong though: Elixir, for example, is statically but weakly typed, and supports FO.)&lt;/p&gt;

&lt;p&gt;Luckily, in the languages listed above (and many others), we can implement the following (example in Java):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Sum&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;);}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;);}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;);}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;);}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;;}&lt;/span&gt; &lt;span class="c1"&gt;// Well... technically, it is&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// And use it...&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Main&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Sum&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Sum&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sum&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// =&amp;gt; Prints 3&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sum&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// =&amp;gt; Prints 3.0&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sum&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// =&amp;gt; Prints 5&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sum&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;7.2&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Prints 7.2&lt;/span&gt;
        &lt;span class="c1"&gt;// But...&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sum&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// =&amp;gt; Compile-time `IllegalArgumentException` - too many arguments&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sum&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, "&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"world!"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// =&amp;gt; Compile-time `IllegalArgumentException` - illegal argument type(s)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method does exactly what we defined earlier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It sums its arguments&lt;/li&gt;
&lt;li&gt;It only accepts arguments of specific shape

&lt;ul&gt;
&lt;li&gt;Note how we had to define, and implement, the cases for "integer-and-float" versus "float-and-integer" separately!&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The compiler knows which implementation of the function to dispatch not due to its first argument, the receiver, &lt;code&gt;s&lt;/code&gt;, since it's the same one for all these functions, but rather by knowing what arguments were provided and dispatching the implementation that takes that arguments' shape.&lt;/p&gt;

&lt;p&gt;Now, contrast that with Julia's multiple dispatch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; sum&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; sum&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; sum&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;float&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; sum&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;float&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; sum&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; sum&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Are you kidding me?
&lt;/h2&gt;

&lt;p&gt;Yes, I admit, it does kinda seem like I'm making a fool of you.&lt;br&gt;&lt;br&gt;
I mean, other than the expected syntax differences, those implementations are virtually identical, down right to having to define, and implement, a separate case for the int-and-float versus float-and-int shapes.&lt;/p&gt;

&lt;p&gt;But you shouldn't be surprised.&lt;br&gt;&lt;br&gt;
After all, conceptually both what FO and MD were developed to do was the same: allow us to develop many &lt;strong&gt;finite&lt;/strong&gt; implementations of the same function, according to its possible inputs.&lt;/p&gt;

&lt;p&gt;So, what &lt;strong&gt;is&lt;/strong&gt; the difference?!&lt;br&gt;&lt;br&gt;
The answer, and the root difference, lies in how each of this two features, is implemented, at what part of the code execution, and what are the costs.&lt;/p&gt;
&lt;h2&gt;
  
  
  Slow and steady wins the race?
&lt;/h2&gt;

&lt;p&gt;As mentioned, FO is &lt;strong&gt;only possible&lt;/strong&gt; in compiled language with static type system.&lt;/p&gt;

&lt;p&gt;When the compiler encounters a method for the first time it compiles it and keeps a reference to the compiled form in its functions lookup table.&lt;br&gt;&lt;br&gt;
To that extent, encounter a function for the first time is &lt;strong&gt;both&lt;/strong&gt; the function's name (but not its receiver), &lt;strong&gt;and&lt;/strong&gt; its inputs' shape.&lt;/p&gt;

&lt;p&gt;It's allowed for a class to have several implementations of the same method, as long as their inputs differ.&lt;br&gt;&lt;br&gt;
The compiler is dutiful and obedient: it sees a method that's not already on the lookup table - it compiles the method.&lt;/p&gt;

&lt;p&gt;Simple.&lt;/p&gt;

&lt;p&gt;And costly: as mentioned, there might be the case that a certain implementation is never actually called.&lt;br&gt;&lt;br&gt;
Yet the compiler took the time to compile it, and now holds a reference to it in its memory.&lt;br&gt;&lt;br&gt;
Both time and memory have been effectively wasted!&lt;/p&gt;

&lt;p&gt;Even though the compiler is what's referred to as "Ahead of Time" (AOT), meaning, among other things, that it knows all the code at compile-time, and knows whether a method is, or is not, invoked - it doesn't matter.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Everything&lt;/strong&gt; is compiled.&lt;/p&gt;
&lt;h2&gt;
  
  
  Fast and loose
&lt;/h2&gt;

&lt;p&gt;With multiple dispatch things are vastly different!&lt;/p&gt;

&lt;p&gt;To begin with, MD is only available in interpreted languages, which should make sense as we just mentioned the compiler of compiled languages "assimilates" all code at compile-time.&lt;/p&gt;

&lt;p&gt;Since interpreted languages parse the code line-by-line (with the exceptions of flow control structures), nothing is pre-compiled and computed.&lt;/p&gt;

&lt;p&gt;When the Julia interpreter encounters a function it checks against its lookup table for a function with the same name and same arguments' shape.&lt;br&gt;&lt;br&gt;
If it isn't found on the table the function is compiled in a manner known as "Just in Time" (JIT), which is exactly what it sounds like: it's compiled just in time for it to be used.&lt;br&gt;&lt;br&gt;
A reference to this compiled instance is then stored on the lookup table, so the next time the same function is encountered the performance would be comparable to an AOT-compiled function call.  &lt;/p&gt;

&lt;p&gt;And, just to make it abundantly clear: &lt;strong&gt;only&lt;/strong&gt; when the interpreter encounters a new function for the first time, does it compile it. As long as we're only calling functions already met or functions from the core library nothing gets compiled, and all functions calls are O(1), as they're already on the functions lookup table of the interpreter.&lt;/p&gt;
&lt;h2&gt;
  
  
  They STILL look like the same picture
&lt;/h2&gt;

&lt;p&gt;So, what's the gain? It &lt;strong&gt;still&lt;/strong&gt; sounds like a "to-mato/to-ma-to" argument.&lt;/p&gt;

&lt;p&gt;Let me say it again, and if it still doesn't click for you, think it over for a second: in MD languages there no work is done until a function is first encountered: no CPU cycles are used to handle, e.g., tokenize, parse, compile, store a reference, etc., a function that may never even be called, no memory is wasted holding a reference to a function that, from the interpreter's point-of-view, doesn't even exist.&lt;/p&gt;

&lt;p&gt;Yes, there is a price to pay on initial encounter of the function: JIT compilation is usually a bit pricier than AOT compilation because a JIT compiler lacks knowledge about the &lt;strong&gt;entire&lt;/strong&gt; code, that AOT compilers have, but that's a rather small price to pay... as can be evident by Julia's performance that routinely matches that of C, though that's also due to Julia's extremely smart design and implementation, employing every performance optimizing trick in the book, on top of MD.&lt;/p&gt;

&lt;p&gt;And then again, Julia programs don't have to wait for compilation to finish. Or start, for that matter. They just run... 🤪&lt;/p&gt;

&lt;p&gt;Another forte of MD is its built-in propensity for "monkey patching" - extending code we don't have direct control of, e.g., a library:&lt;br&gt;&lt;br&gt;
Assume a library we use exports a given function &lt;code&gt;utilFunc&lt;/code&gt;. The function however is defined only for certain arguments's shapes, but we want to use it with our own arguments' shapes.&lt;/p&gt;

&lt;p&gt;In languages like C# we'd have to use "extension methods", a hack that was invented exactly for this purpose, and like all workarounds it feels bolted on the language syntax, and is verbose to code.&lt;/p&gt;

&lt;p&gt;In Julia we simply add &lt;strong&gt;in our own&lt;/strong&gt; code something alone the lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight julia"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="nf"&gt; utilFunc&lt;/span&gt;&lt;span class="x"&gt;(&lt;/span&gt;&lt;span class="n"&gt;our&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;Float64&lt;/span&gt;&lt;span class="x"&gt;,&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="x"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# Implementation goes here&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Et voila. That simple.&lt;br&gt;&lt;br&gt;
We don't need the library's source, in fact the only reason we're naming our function the same as the library's is because they serve the same purpose so it makes sense.&lt;br&gt;&lt;br&gt;
In actuality, and unlike extension methods in C#, our function could've been called whatever name we wanted, and it would work the same. Zero coupling!&lt;/p&gt;

&lt;h2&gt;
  
  
  Why don't they build the entire airplane from the materials of the black box?
&lt;/h2&gt;

&lt;p&gt;A question comes to mind, "if MD is so good, why aren't all programming languages implementing it? Especially if, as Julia proves, you can have an interpreted, JIT-compiled, dynamic, language, and get C-like performance..."&lt;/p&gt;

&lt;p&gt;And the answer is twofold.&lt;/p&gt;

&lt;p&gt;First, as mentioned, Julia's performance is not &lt;strong&gt;only&lt;/strong&gt; due to MD. Sure, it helps, but under Julia's hood there is some remarkable software engineering. Julia's maintainers took every page of the "How to make computers faster" book... and then wrote a few more chapters themselves.&lt;/p&gt;

&lt;p&gt;Second, safety.&lt;br&gt;&lt;br&gt;
For the small(?) price of AOT compilation and its consequences, statically typed languages offer the developer something an interpreted language simply can't: compile-time type safety!&lt;br&gt;&lt;br&gt;
Really is that simple. Personally, I'm more than willing to pay the "AOT tax" when using my loved F#, knowing the compiler will smack me on the nose anytime I try to do something stupid in my code.&lt;br&gt;&lt;br&gt;
Is it worth it? I think so. Others may disagree. I like my types strong and static.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fin
&lt;/h2&gt;

&lt;p&gt;That's it.&lt;br&gt;&lt;br&gt;
Not quite two sides of the same coin, they're two ways to implement the same concept in differing environments.&lt;/p&gt;

&lt;p&gt;I hope you now have a better understanding of the difference between multiple dispatch and function overloading than when you started reading this post.&lt;/p&gt;

</description>
      <category>computerscience</category>
      <category>learning</category>
      <category>programming</category>
    </item>
    <item>
      <title>Say what you mean</title>
      <dc:creator>O.F.K.</dc:creator>
      <pubDate>Sun, 21 Sep 2025 11:25:40 +0000</pubDate>
      <link>https://dev.to/omanfk/say-what-you-mean-2ehc</link>
      <guid>https://dev.to/omanfk/say-what-you-mean-2ehc</guid>
      <description>&lt;h2&gt;
  
  
  Use your words
&lt;/h2&gt;

&lt;p&gt;Most programming languages today, barring EsoLangs and the APL family of languages, use English for their APIs.&lt;br&gt;&lt;br&gt;
Some languages' designer, like for Python and Ruby, went to great lengths to make those languages' APIs read like a valid English sentence (more or less.)&lt;/p&gt;

&lt;p&gt;The thing with using a spoken language for APIs to be consumed by a computer, is that sometimes the meaning of words in the natural language get hijacked into meaning something not quite the same.&lt;br&gt;&lt;br&gt;
A great example for that is the word 'private'.&lt;/p&gt;

&lt;p&gt;In English, the word 'private' means one very particular thing: something that is personal, not to be interacted with by anyone other than its owner. Not quite, but not too far removed from a secret.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's the point?
&lt;/h2&gt;

&lt;p&gt;Lately, as part of my Elixir-to-F# reimplementation journey, I defined several private types.&lt;br&gt;&lt;br&gt;
In F# when a type is private it requires a public constructor to create instances of, but it &lt;strong&gt;also&lt;/strong&gt; requires a &lt;strong&gt;public&lt;/strong&gt; getter to access its members, so they can be viewed or updated.&lt;br&gt;&lt;br&gt;
All that added boilerplate got me thinking, "is it worth it?!"&lt;/p&gt;

&lt;p&gt;When I created the private types, and their &lt;strong&gt;public&lt;/strong&gt; constructors, I had a notion of the word 'private' like that in the English language: the type is a secret, any attempt to create an instance of it must be allowed by the type owner - me.&lt;br&gt;&lt;br&gt;
No team member, and for that matter me, two months in the future, having forgotten the context and implementation details, is such a "team member", can create an instance of the private type without my say so - no instances showing up in places I don't expect them.&lt;/p&gt;

&lt;p&gt;But then it struck me: the constructor is public!&lt;br&gt;&lt;br&gt;
Any member can easily just code &lt;code&gt;privateType.create args&lt;/code&gt; and have an instance of that type, no permission needed.&lt;br&gt;&lt;br&gt;
Rogue instances can crop up everywhere in my otherwise pristine code!&lt;br&gt;&lt;br&gt;
What's the point?! What did I gain by making the type private?&lt;/p&gt;

&lt;p&gt;And more importantly, why is my favorite language lying to me?&lt;br&gt;&lt;br&gt;
"Private" doesn't mean secret, only accessible by whom I trust and allow.&lt;/p&gt;

&lt;p&gt;It means something else!&lt;/p&gt;

&lt;h2&gt;
  
  
  A trusted gatekeeper
&lt;/h2&gt;

&lt;p&gt;So I asked ChatGPT, obviously.&lt;br&gt;&lt;br&gt;
And the answer I got really made sense to me, so I'm sharing it here in my own words and style.&lt;/p&gt;

&lt;p&gt;In programming, and that's true for all languages that implement the "private" accessibility control, no matter what it is actually called, that words means something not quite the same as the English meaning.&lt;br&gt;&lt;br&gt;
Close, that's why it was chosen, but not entirely the same.&lt;/p&gt;

&lt;p&gt;In programming when you define something as private, and assuming you &lt;strong&gt;do&lt;/strong&gt; want it to be accessible to the other code, you &lt;strong&gt;must&lt;/strong&gt; also provide APIs (e.g., methods, functions, etc.) to create and access that something. Those implementations &lt;strong&gt;must&lt;/strong&gt; be public.&lt;/p&gt;

&lt;p&gt;But, how those public APIs look like and behave... that's entirely up to the implementor!&lt;/p&gt;

&lt;p&gt;Are you already seeing where I'm going with this? What's the gain?&lt;br&gt;&lt;br&gt;
By controlling the only ways to create and get the private "something", we make sure that whoever is creating and getting is only able to create and get what we want them to!&lt;/p&gt;

&lt;p&gt;For example, in one of my private types the age must be a positive integer, greater than 0.&lt;br&gt;&lt;br&gt;
By making the type private and only exposing a public constructor of my doing, I made sure that no team member can just do &lt;code&gt;privateType (age=-10)&lt;/code&gt; and create an invalid instance of that type.&lt;br&gt;&lt;br&gt;
Oh, no. The only way to get an instance is doing &lt;code&gt;privateType.create (age=XYZ)&lt;/code&gt;, and if that &lt;code&gt;XYZ&lt;/code&gt; is invalid, e.g., negative integer or zero, the instance will simply not be created.&lt;br&gt;&lt;br&gt;
(What happens then is &lt;strong&gt;also&lt;/strong&gt; the responsibility of the &lt;strong&gt;private&lt;/strong&gt; type - no concern of whomever is trying to instantiate the type!)&lt;/p&gt;

&lt;p&gt;Same for getting data from the instance.&lt;br&gt;&lt;br&gt;
No longer can anyone do &lt;code&gt;privateTypeInstance.internalDataNooneShouldHaveAccessTo&lt;/code&gt;!&lt;br&gt;&lt;br&gt;
The getter is public, sure, but I, the implementor of it, decide what data is accessible through it.&lt;/p&gt;

&lt;p&gt;"Private" in programming means something on the lines of a trusted gatekeeper: it only allows interactions with the data it guards in certain, permissible, ways.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, the same as the English definition?
&lt;/h2&gt;

&lt;p&gt;Coming to think about it, looking at my own definition of the word "private" in English, I &lt;strong&gt;can&lt;/strong&gt; see the resemblance.&lt;br&gt;&lt;br&gt;
As in English so in programming, "private" denotes something to be kept away from the public, not interacted with by those not permitted.&lt;br&gt;&lt;br&gt;
But unlike English, it doesn't mean a secret, simply something that is fenced off, and only allowed access via the gate, that is being guarded by a trusted guard.&lt;/p&gt;

&lt;p&gt;The issue of what's to gain has given me a lot of headaches in the last few days, trying to understand what's to gain by it.&lt;br&gt;&lt;br&gt;
If you had that same question, hope I just cleared it away for you (otherwise, I'm sure ChatGPT can. 🤪)&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>softwareengineering</category>
      <category>designpatterns</category>
    </item>
    <item>
      <title>My hovercraft is full of Rubies</title>
      <dc:creator>O.F.K.</dc:creator>
      <pubDate>Thu, 18 Sep 2025 14:13:51 +0000</pubDate>
      <link>https://dev.to/omanfk/my-hovercraft-is-full-of-rubies-16j7</link>
      <guid>https://dev.to/omanfk/my-hovercraft-is-full-of-rubies-16j7</guid>
      <description>&lt;h2&gt;
  
  
  Ruby, the Python we deserved
&lt;/h2&gt;

&lt;p&gt;My first "real" programming language, if we don't count Matlab and Mathematica (today, Wolfram language) I used in school while majoring chemistry, was Ruby, which is why I guess have I a soft spot for it even today.&lt;/p&gt;

&lt;p&gt;I'm also well aware there is no real contest any more between Ruby and Python, the latter having taken over the programming world by storm, while the former is diminishing by the day, sadly.&lt;/p&gt;

&lt;p&gt;Yet I still think Ruby is the better dynamic, "scripting", programming language.&lt;br&gt;&lt;br&gt;
The Python we deserved, but not going to get.&lt;/p&gt;
&lt;h2&gt;
  
  
  Yeah, well, you know, that's just like YOUR opinion, man
&lt;/h2&gt;

&lt;p&gt;Yes, &lt;a href="https://youtu.be/6yfvuPk8xd4?t=105" rel="noopener noreferrer"&gt;Dude&lt;/a&gt;, it is. Not too surprising as this is my blog. 🤪&lt;br&gt;&lt;br&gt;
Of course every opinion here is mine, and only mine. You can agree, or disagree, it's your right.&lt;/p&gt;

&lt;p&gt;I just hope whether you're a Pythonista, a Rubyist, or a Gopher (FSM have mercy on your soul), you think about what I'm saying before you'll dismiss me and my writing as nonsense.&lt;/p&gt;
&lt;h2&gt;
  
  
  Objects all the way down, consistently
&lt;/h2&gt;

&lt;p&gt;The most glaring, not sole, but definitely the poster child of Python's wrong design is the ambiguity between its pronounced "Everything is an object" paradigm, and its implementation, especially with regard to built-in functions.&lt;/p&gt;

&lt;p&gt;Take for example the function to get an iterable object's length, of which the most common is an array, or list as it's known in Python (in itself a bad decision: a list in computer science has come to denote a singly-linked list. Python's lists are what other languages call array, and for a good reason: they denote a &lt;strong&gt;contiguous&lt;/strong&gt; memory section.) The Python code is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;myLst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myLst&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This begs the question, "why?!?!"&lt;br&gt;&lt;br&gt;
Especially given that other list &lt;strong&gt;methods&lt;/strong&gt;, e.g., appending, &lt;strong&gt;are&lt;/strong&gt; accessed in the "dot-method" syntax, i.e., &lt;code&gt;myLst.append(10)&lt;/code&gt;, and &lt;strong&gt;are defined&lt;/strong&gt; on the list class.&lt;/p&gt;

&lt;p&gt;In contrast, in Ruby:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;myArr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;myArr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="c1"&gt;# Can also use `myArr.size`, and even `myArr.count` (without any arguments to the `count` method)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The answer to the question "why?" is somewhat interesting.&lt;br&gt;&lt;br&gt;
In Python everything &lt;strong&gt;is&lt;/strong&gt; indeed an object, lists included, and every object has its own methods.&lt;br&gt;&lt;br&gt;
There are, however, certain &lt;strong&gt;generic functions&lt;/strong&gt;, &lt;strong&gt;not&lt;/strong&gt; methods, that are not defined on any specific class.&lt;br&gt;&lt;br&gt;
Because those functions may be used on many data structures, of differing types, it was decided to group them inside a special dictionary: &lt;code&gt;__builtins__&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When a function is called that is not in any other scope, the Python interpreter looks those functions in that special dictionary, and if it is indeed there, the built-in function being referenced by the key is called.&lt;br&gt;&lt;br&gt;
&lt;code&gt;len()&lt;/code&gt; is one such function, and any object that implements the &lt;code&gt;__len__&lt;/code&gt; "magic method", as lists do, can call it (which, for lists, is exactly what the magic method does!)&lt;/p&gt;

&lt;p&gt;Ruby, on the other hand, has its roots firmly in the Smalltalk programming language - the original language to coin such term as "class", "object", and "object-oriented"!&lt;br&gt;&lt;br&gt;
A core tenant of Smalltalk was how it defined objects: a data container that can only communicate with its environment via message passing. When an object receives a message it checks against its own finite list of known messages. If the incoming message is on its list, it carries out the corresponding action.&lt;br&gt;&lt;br&gt;
(Smalltalk's messages are not the same as today's methods since they're external to the object, and are more in tune with modern actor frameworks, but the concept is valid.)&lt;/p&gt;

&lt;p&gt;It's clear, therefore, that in Ruby, &lt;code&gt;length&lt;/code&gt; (or &lt;code&gt;size&lt;/code&gt;, or &lt;code&gt;count&lt;/code&gt;) are methods on the array object. Also on, for example, the string object too!&lt;br&gt;&lt;br&gt;
Each class that needs its derived objects to return their size implements the appropriate methods, in a way that's correct for it.&lt;br&gt;&lt;br&gt;
There's no one, central, generic, function because that would mean that an array object wouldn't know how to respond to a &lt;code&gt;length&lt;/code&gt; method call passed to it!&lt;/p&gt;

&lt;p&gt;The difference is less pronounced in, for example, mathematics where addition in both languages is &lt;strong&gt;implemented as a method&lt;/strong&gt; on the corresponding numeric class with the &lt;code&gt;+&lt;/code&gt; operator being syntactic sugar over a method call.&lt;br&gt;&lt;br&gt;
For example in Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; (3).__add__(5), the `add` methodn is implemented on the int class
&lt;/span&gt;
&lt;span class="mf"&gt;3.0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;5.0&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; (3.0).__add__(5.0), the `add` method is implemented, seperately, on the float class, too!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in Ruby:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; 3.+(5), `+` is the **actual** method name, implemented on the Integer class&lt;/span&gt;

&lt;span class="mf"&gt;3.0&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;5.0&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; 3.0.+(5.0), `+` is the **actual** method name, implemented, seperately, on the Float class, too!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I feel like Ruby's way of implementing object-orientation, rather Smalltalk's way, is better: whenever you "dot into" an object in your IDE you get the &lt;strong&gt;full list&lt;/strong&gt; of what the object is capable of handling. No "magic" functions hidden in a global scope to have to guess.&lt;br&gt;&lt;br&gt;
Truly, "What You See Is What You Get" (WYSIWYG).&lt;/p&gt;
&lt;h2&gt;
  
  
  "Come up and see me, make me smile"
&lt;/h2&gt;

&lt;p&gt;One of Matz's (Dr. Yukihiro Matsumoto, Ruby's creator) stated goals in inventing Ruby was to "optimized for programmer's happiness", what now days is known as "Developer Experience" (DX).&lt;br&gt;&lt;br&gt;
To that end, Ruby has, from inception, been a language more concerned with convention over anything else: if the community figured out that doing some task is best achieved in a particular way, that way became the norm. Matz never tried enforcing his opinion on the shaping of the language and its use.&lt;/p&gt;

&lt;p&gt;More over, even if a certain way &lt;strong&gt;did&lt;/strong&gt; become convention, Ruby almost always offers more ways of getting things done, as can be seen in the previous array length example: there are 3 such ways. Granted, two are simply an alias of each other, while the third is a bastardization of a different method all together, but &lt;strong&gt;there are options&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Another, complementary, goal of Ruby was "the principle of least surprise" (AKA "principle of least astonishment"): for a &lt;strong&gt;skilled&lt;/strong&gt; developer in Ruby, there should be no code, no behavior, no quirk, that makes the developer go "huh?!". Once someone becomes acquainted with Ruby, coding in it should feel streamlined because one can reason about the code before even coding in a predictable way!&lt;br&gt;&lt;br&gt;
(Contrast that notion with, say, JavaScript, and nowadays TypeScript too, as can be seen in the now classic &lt;a href="https://www.destroyallsoftware.com/talks/wat" rel="noopener noreferrer"&gt;WAT&lt;/a&gt; video.)&lt;/p&gt;

&lt;p&gt;Python's creator, Dr. Guido van Rossum, had another idea about what constitutes developer's happiness, and in Python we have &lt;a href="https://en.wikipedia.org/wiki/Zen_of_Python" rel="noopener noreferrer"&gt;"The Zen of Python"&lt;/a&gt; - a set of 19 dictates how Python code should be laid out and coded, that has some very strict ideas.&lt;br&gt;&lt;br&gt;
The Zen of Python was considered such an important part of the language's design that it was made into the &lt;strong&gt;20th&lt;/strong&gt; official PEP - Python Enchancment Proposal, the official way the community can &lt;strong&gt;suggest&lt;/strong&gt; improvements to the language to van Rossum, who takes a very active charge of his creation's evolution, being its BDFL - benevolent dictator for life.&lt;br&gt;&lt;br&gt;
The Zen of Python is so central to the language that in the Python REPL you can view it by typing &lt;code&gt;import this&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Personally I prefer Ruby's convention-by-community over Python's "there should be one, preferably only one, obvious way of doing things".&lt;br&gt;&lt;br&gt;
Coding is a creative process as much as it is a technical endeavor. Ruby encourages freedom and exploration, Python leans toward strict correctness.&lt;/p&gt;
&lt;h2&gt;
  
  
  I see what you mean
&lt;/h2&gt;

&lt;p&gt;One complaint anybody who ever coded in Java has about it is how verbose it is.&lt;br&gt;&lt;br&gt;
Well, neither Ruby nor Python are as verbose as Java, thank the FSM, actually feeling more dynamic while being on the JVM was one design goal of the Groovy programming language, but between them there's a clear winner for succinctness.&lt;br&gt;&lt;br&gt;
(Yes, I am well aware of the irony, me talking about succinctness. I like it in my programming languages, not in my own writing. 🤣)&lt;/p&gt;

&lt;p&gt;Not so much about LoCs, but rather about readability, a few examples will clarify the subject.&lt;/p&gt;

&lt;p&gt;We'll start off with something simple: read in a text file, and print the 3 most frequent words in it.&lt;br&gt;&lt;br&gt;
In Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;\w+&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
        &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# Normalize for sotring and getting from the dictionary
&lt;/span&gt;        &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;top3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])[:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;top3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And Ruby:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Ruby's `File.read` closes the file once out of scope, as does Python's `with`, but that's external to the file opening: `open(fileName)`&lt;/span&gt;
&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"input.txt"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\w+/&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;downcase&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;# `w.downcase` is the normalizer as in the Python example`&lt;/span&gt;
&lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort_by&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By the way, did you catch how in Ruby the last expression is returned by default? No need to remember to call &lt;code&gt;return&lt;/code&gt;... or bang your head in frustration, trying to debug the code, figuring out why our function returns &lt;code&gt;None&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Another all too common example: initializing a class constructor with instance variables.&lt;br&gt;&lt;br&gt;
In Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And Ruby:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="vi"&gt;@name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;
        &lt;span class="vi"&gt;@age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While one may comment on Ruby's use of &lt;code&gt;def...end&lt;/code&gt;, I like its human-readable &lt;code&gt;initialize&lt;/code&gt; over the magic method &lt;code&gt;__init__&lt;/code&gt;, and the fact that the &lt;code&gt;@&lt;/code&gt; is an instance variable definition, making short change of having to reference &lt;code&gt;self&lt;/code&gt; both in the constructor's parameters, and in assigning them values.&lt;/p&gt;

&lt;p&gt;And, lastly, one more example: given an array of JSON-like objects, find those who are over 18, and capitalize their name.&lt;br&gt;&lt;br&gt;
In Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;people&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;age&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bob&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;age&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;carol&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;age&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="n"&gt;adults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;people&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;age&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;adults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;capitalize&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;adults&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And Ruby:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;people&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;age: &lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"bob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;age: &lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="s2"&gt;"carol"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;age: &lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="n"&gt;adults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;people&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:age&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;capitalize&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;adults&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice also that Ruby supports the notion of symbols as keys in dictionaries, so no need to quote the keys, unlike in JSON, and the keys aren't strings, but symbols which are singletons, making hashing them a touch faster than Python as they're placed in Ruby's symbols table on creation.&lt;br&gt;&lt;br&gt;
Also, visible in the above examples is Ruby's excellent block syntax for anonymous functions: blocks can be chained together to form a compound function, something simply impossible with Python's &lt;code&gt;lambda&lt;/code&gt; expressions.&lt;br&gt;&lt;br&gt;
Block can also be written in a line-by-line fashion, for example, for readability:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;people&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:age&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, this format does &lt;strong&gt;not&lt;/strong&gt; compose.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Functional (Programming) language
&lt;/h2&gt;

&lt;p&gt;Ever since I learned of the Functional Programming paradigm I started detesting the Object-Oriented paradigm (as can be seen by my other post series on transforming code from Elixir, an FP language, to F#, another FP language.)&lt;/p&gt;

&lt;p&gt;Aside from such attributes as immutability by default, FP languages have one more trait that makes them a treat to work with, it's in the name: they're functional. They consider functions as first-class citizens, making passing them as arguments to other functions, getting them back as a result, binding them to a value, etc. as easy as it gets.&lt;/p&gt;

&lt;p&gt;Now, true, Python does allow some FP even at its core, without 3rd-party libraries, chief example of which is the afore mentioned lambda expressions. But that's pretty much the extent of things. A Python list, for example, doesn't have built-in, core library, facilities to map over its elements, filter them, etc.&lt;/p&gt;

&lt;p&gt;Ruby, on the other hand, as we saw before, does. Out-of-the-box.&lt;br&gt;&lt;br&gt;
But Ruby takes it one step further with its &lt;code&gt;Procs&lt;/code&gt;, shorthand for procedures: blocks of code, anonymized functions, that can be bound to a variable and passed as arguments to other functions (that can then call on the procedure using the keyword &lt;code&gt;yield&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# First a simple example, directly calling a `Proc`, also showing the `.select` (AKA `.filter`) functionality of arrays&lt;/span&gt;
&lt;span class="n"&gt;isEven?&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;even?&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;evenNums&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;isEven?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# More involved: the `Proc` is passed to a **method** (here on the `Global` object as an argument)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;processNums&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="c1"&gt;# This calls the `Proc`&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;isEven?&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;even?&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;processNums&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;isEven?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# `Proc` pass explicitly, even though not in function's signature, because function expects it!&lt;/span&gt;

&lt;span class="c1"&gt;# We don't like signatures to "lie". Let's fix that&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;processNums&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="nb"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;isEven?&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;even?&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;processNums&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;isEven?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It was actually in Ruby that I first grasped the power of Functional Programming, even though at the time I didn't know that's what it's called.&lt;br&gt;&lt;br&gt;
Ruby is a very much OO language, as we saw in an earlier section, but it always had, since its beginning, a strong FP streak to it which amplifies the joy of its developers, in line with its design goal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Eat my dust
&lt;/h2&gt;

&lt;p&gt;And now we should probably talk about performance, and, as much as I don't like admitting it, performance-wise, Python always had, still does, and probably always will have, the upper-hand.&lt;/p&gt;

&lt;p&gt;Of course, if hyper-performance is an issue, neither language is a good fit, as they're both interpreted languages that require runtime compilation (i.e., JIT compilation) via their respective virtual machines.&lt;br&gt;&lt;br&gt;
But, assuming a subjectively "reasonable" performance is sufficient, yeah... Python usually beats Ruby by a factor of x2-3.&lt;br&gt;&lt;br&gt;
(I'm referring to the canonical implementations of each: CPython for Python and MRI for Ruby.&lt;br&gt;&lt;br&gt;
Experimental compilers such as PyPy for Python and TruffleRuby for Ruby are cool, but are not mainstream, and in that case, TruffleRuby, running on the GraalVM comes very close to native speeds!)&lt;/p&gt;

&lt;h2&gt;
  
  
  The way of the world
&lt;/h2&gt;

&lt;p&gt;Ruby just got a bad deal, and its community missed out on opportunities.&lt;br&gt;&lt;br&gt;
Originating in Japan in a time when commercial internet was just budding, and being created by a reserved, soft-spoken Japanese man, in line with his culture and heritage, Ruby was not well positioned to take over the world by storm. It didn't help that for a very long time its official documentation were only in Japanese, either.&lt;/p&gt;

&lt;p&gt;Contrast that with Python being developed by a Dutch with no reservations about promoting his creation, in the middle of Europe at a time when a good, succinct, relatively performant alternative to Java, itself an infant back then, not the performance powerhouse it is today, was sought for.&lt;/p&gt;

&lt;p&gt;Ruby's community also missed out: after creating Ruby on Rails, which for a time dominated the web framework scene the community rested on its laurels, oblivious to how data science was picking up steam.&lt;br&gt;&lt;br&gt;
Python's community, on the other hand, saw clear and started creating multiple, competing DS libraries, and in the process pushing each library to become better, until the efforts were consolidated into a single DS stack that we know today (e.g., NumPy, Pandas, SciKit-learn, Numba, Matplotlib, PyTorch, and others.)&lt;br&gt;&lt;br&gt;
Python's grasp on the DS community is so strong today that even specialized languages for doing DS like Julia and R are losing the fight to Python, if they ever stood a chance to begin with.&lt;/p&gt;

&lt;p&gt;As Python is rising, for DS, for web frameworks, for backend business logic, Ruby is declining.&lt;/p&gt;

&lt;p&gt;That's just the way of the world: bad luck, bad timing, and yes, bad decision-making make for the suboptimal alternative taking the lead. We've seen it happening so many times, what's another one?&lt;/p&gt;

&lt;p&gt;Personally in my hobby-coding and side projects, I've moved to Functional Programming and never looked back.&lt;br&gt;&lt;br&gt;
But if I was asked to start a new, greenfield, project today and use one of the more orthodox languages, I wouldn't take JS, nor TS, not Go (yeccch!), and no, not Python either.&lt;br&gt;&lt;br&gt;
I'd vote for Ruby knowing it will do the project proud, and give its developers joy.&lt;/p&gt;

</description>
      <category>python</category>
      <category>ruby</category>
      <category>oop</category>
    </item>
    <item>
      <title>Death of a lens(man)</title>
      <dc:creator>O.F.K.</dc:creator>
      <pubDate>Sun, 14 Sep 2025 10:36:38 +0000</pubDate>
      <link>https://dev.to/omanfk/death-of-a-lensman-5be8</link>
      <guid>https://dev.to/omanfk/death-of-a-lensman-5be8</guid>
      <description>&lt;h2&gt;
  
  
  Focusing on the issue
&lt;/h2&gt;

&lt;p&gt;Taking a break from my Elixir-to-F# implementation, I encountered an issue when dealing with some other code I had lying around, that made me think, and admire, how F#'s syntax evolved lately, in response to actual problems folks developing in it had - a real living language, that puts its users' needs front and center.&lt;/p&gt;

&lt;p&gt;Imagine an F# domain that has a deeply nested record structure, e.g. record-of-record-of-record..., not too far removed from a JSON of JSONs, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Indeed, this domain is as contrived as they get, and excessively sub-optimal... a perfect example!&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;PersonName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nc"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nc"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;(*
Not related to the post's topic, but `HouseNumber` is a `string`! **Never** use numeric types (e.g., int, float, decimal) to denote quantities that can't be acted upon mathematically, even if they are **represented** as numbers. Numeric types should only be assigned to numeric quantities! A valid exception is `Id`, and even then, in production, prefer using `System.Guid`
*)&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Locale&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nc"&gt;Street&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nc"&gt;HouseNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nc"&gt;City&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nc"&gt;Locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Locale&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Details&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nc"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PersonName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nc"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nc"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nc"&gt;Details&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Details&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sherlockHolmes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="nc"&gt;Details&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;FirstName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Sherlock"&lt;/span&gt;
            &lt;span class="nc"&gt;LastName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Holmes"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;Address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;City&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"London"&lt;/span&gt;
            &lt;span class="nc"&gt;Locale&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;Street&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Baker"&lt;/span&gt;
                &lt;span class="nc"&gt;HouseNumber&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"221B"&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, suppose the good detective decided to rent a more cost-effective apartment on the opposite side of the street, making his new address 222 Baker street.&lt;br&gt;&lt;br&gt;
How would we represent this in our data structure?&lt;/p&gt;
&lt;h2&gt;
  
  
  Seeing what the problem is
&lt;/h2&gt;

&lt;p&gt;An interesting question to ask is "why is that a problem in the first place? Just update the nested member and be done with it!"... which is a great suggestion, really, no cynicism implied, except that in F# that used to be not exactly straightforward. To put it mildly.&lt;/p&gt;

&lt;p&gt;The thing is records in F#, like all F#-native data structures, are immutable.&lt;br&gt;&lt;br&gt;
That means a couple of things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When "updating" the dear detective, we will &lt;strong&gt;not&lt;/strong&gt; be updating the current memory object that holds the data, instead we'd be creating a new data structure that is a clone of the current one, with any of our updates applied to said new data structure.&lt;/li&gt;
&lt;li&gt;There is no way to tell the F# compiler "take this object and update the &lt;code&gt;HouseNumber&lt;/code&gt; member, that is three-levels deep nested, leaving the rest of the data intact". This operation, easily done in C# with a single line of code, albeit a long-one, it is three-levels deep, would take us in F# considerable effort.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Eyeing the solution
&lt;/h2&gt;

&lt;p&gt;In the "classic" F# syntax, prior to version 8, and of course, still valid today for the "classist" amongst us, the way to do that would have been:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sherlockHolmesUpdated&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;sherlockHolmes&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
        &lt;span class="nc"&gt;Details&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;sherlockHolmes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Details&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
                &lt;span class="nc"&gt;Address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;sherlockHolmes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Details&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Address&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
                        &lt;span class="nc"&gt;Locale&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="n"&gt;sherlockHolmes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Details&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Locale&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
                                &lt;span class="nc"&gt;HouseNumber&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"222"&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is, using F#'s record update method we need to specify &lt;strong&gt;all&lt;/strong&gt; the nested level up to and including the level we want to change. As the change is more deeply nested, the number of time we need to specify the entire chain grows, leading to a very accurate, true, but highly unreadable "pyramid of change", so to speak.&lt;/p&gt;

&lt;p&gt;Surely, there is a better way of doing this?&lt;/p&gt;

&lt;h2&gt;
  
  
  Seeing with pinpoint accuracy
&lt;/h2&gt;

&lt;p&gt;Well, the answer is "yes, of course". But also "not quite, no".&lt;br&gt;&lt;br&gt;
Which is a great time to introduce the subject of &lt;strong&gt;optics&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In Functional Programming languages, optics is a name given to a set of functions dealing with nested data structures. The tongue-in-cheek reasoning is that "optics let the user focus their code with pinpoint accuracy on the data of interest".&lt;/p&gt;

&lt;p&gt;I will &lt;strong&gt;not&lt;/strong&gt; be explaining optics in the post, but we need to know they exist, check, and that the first such pattern, the most used of them all is &lt;code&gt;lenses&lt;/code&gt;, that allow getting, and setting, data in said highly-nested data structures. Check too.&lt;/p&gt;

&lt;p&gt;The rub? Well, I said it... optics libraries need to be implemented before we can use them, they're not built-in into the language!&lt;br&gt;&lt;br&gt;
Indeed, how could they? There's no telling what are data structures would look like.&lt;/p&gt;

&lt;p&gt;Even the existing libraries don't implement &lt;strong&gt;specific&lt;/strong&gt; lenses, but rather give the user a framework to implement their own, according to their data.&lt;/p&gt;

&lt;p&gt;I'm keeping the discussion on lenses, and optics in general, vague and hand-wavy on purpose: it's a huge one, complex, and while using them isn't too hard to grok, implementing them is worse than a root canal without anesthesia!&lt;br&gt;&lt;br&gt;
Still, for those interested: you can &lt;a href="https://dev.to/choc13/grokking-lenses-2jgp"&gt;read&lt;/a&gt;, some &lt;a href="https://dev.to/rockfire/simplifying-data-manipulation-in-net-f-with-aether-a-practical-guide-to-using-lenses-prisms-5092"&gt;more&lt;/a&gt;, and &lt;a href="https://medium.com/@heytherewill/functional-programming-optics-in-net-7e1998bfb47e" rel="noopener noreferrer"&gt;here&lt;/a&gt; too, for starter.&lt;br&gt;&lt;br&gt;
(For the very inquisitive minds, &lt;a href="https://fsprojects.github.io/FSharpPlus" rel="noopener noreferrer"&gt;FSharpPlus&lt;/a&gt; has a very robust optics module, one such sub-module is &lt;a href="https://fsprojects.github.io/FSharpPlus/lens.html" rel="noopener noreferrer"&gt;Lens&lt;/a&gt;. You can look up the code on GitHub and see just how complex implementing a &lt;code&gt;lens&lt;/code&gt; &lt;strong&gt;framework&lt;/strong&gt; is.&lt;br&gt;&lt;br&gt;
Let's just say that &lt;a href="https://fsharpforfunandprofit.com/posts/elevated-world-7/" rel="noopener noreferrer"&gt;applicatives&lt;/a&gt; is the least complex implementation detail!)&lt;/p&gt;
&lt;h2&gt;
  
  
  Rose-tinted glasses to all
&lt;/h2&gt;

&lt;p&gt;So, updating deeply nested data is a chore in F#, whether using the classic approach, or using a lens library and implementing them for our specific data using the framework afforded by the library.&lt;/p&gt;

&lt;p&gt;That was true, as I hinted earlier, till F#8 (for reference on November 2025 Microsoft will release F#10) when an update to the syntax of the most used case of the optics, the lens, was delivered.&lt;/p&gt;

&lt;p&gt;Let's see just how easy it is to let dear Sherlock move out now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sherlockHolmesUpdated&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;sherlockHolmes&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
        &lt;span class="nn"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Details&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Locale&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HouseNumber&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"222"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yup, that's it!&lt;br&gt;&lt;br&gt;
Using the "classic" record update syntax, instead of updating each nested level on its own, we now simply build a chain of the nested levels up to the change location and... that's it, presto magic!&lt;br&gt;&lt;br&gt;
The only caveat in this new syntax is that it must start at the top-most level of nesting: Sherlock is an &lt;code&gt;Employee&lt;/code&gt;, so we must start our chain with it (some texts seem to omit this initial level, which will result in type error.)&lt;/p&gt;

&lt;p&gt;Multiple changes in one go also behave the same:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sherlockInLiverpool&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;sherlockHolmes&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
        &lt;span class="nn"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Details&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;City&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Liverpool"&lt;/span&gt;
        &lt;span class="nn"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Details&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Locale&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Street&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Something St."&lt;/span&gt;
        &lt;span class="nn"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Details&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Locale&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HouseNumber&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"5"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, for comparison with the classic method, that would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sherlockInLiverpoolClassicApproach&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;sherlockHolmes&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
        &lt;span class="nc"&gt;Details&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;sherlockHolmes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Details&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
                &lt;span class="nc"&gt;Address&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;sherlockHolmes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Details&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Address&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
                        &lt;span class="nc"&gt;City&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Liverpool"&lt;/span&gt;
                        &lt;span class="nc"&gt;Locale&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
                            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;sherlockHolmes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Details&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Locale&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt;
                                &lt;span class="nc"&gt;Street&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Something st."&lt;/span&gt;
                                &lt;span class="nc"&gt;HouseNumber&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"5"&lt;/span&gt;
                            &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Was blind but now can see
&lt;/h2&gt;

&lt;p&gt;The new syntax turned out to be so good at cutting down the burden of updating records that several optics libraries maintainers stopped maintaining their projects.&lt;br&gt;&lt;br&gt;
F# now has imperative-style nested records update method, with clean, comprehensible syntax.&lt;/p&gt;

&lt;p&gt;It's really fun when language maintainers, in the case of F# that would be Microsoft, listen to its users' qualms and issues and fix them (even if it takes some time.)&lt;br&gt;&lt;br&gt;
Which reminds me... eat F#'s shorts, Go! 🤪😂&lt;/p&gt;

</description>
      <category>fsharp</category>
      <category>datastructures</category>
    </item>
    <item>
      <title>One... Two... Testing</title>
      <dc:creator>O.F.K.</dc:creator>
      <pubDate>Sat, 13 Sep 2025 09:59:05 +0000</pubDate>
      <link>https://dev.to/omanfk/one-two-testing-1hab</link>
      <guid>https://dev.to/omanfk/one-two-testing-1hab</guid>
      <description>&lt;h2&gt;
  
  
  Nobody &lt;code&gt;Expecto&lt;/code&gt; the Spanish Inquisition
&lt;/h2&gt;

&lt;p&gt;On our previous hike, we modelled the domain of FunPark - our miniature theme-park management application.&lt;br&gt;&lt;br&gt;
It's wise to also test our code.&lt;/p&gt;

&lt;p&gt;F# has a bunch of testing frameworks: starting from &lt;a href="https://nunit.org/" rel="noopener noreferrer"&gt;NUnit&lt;/a&gt;, the more generic .Net port &lt;a href="https://github.com/xunit/xunit/" rel="noopener noreferrer"&gt;xUnit.net&lt;/a&gt;, going through &lt;a href="https://fsprojects.github.io/FsUnit/" rel="noopener noreferrer"&gt;FsUnit&lt;/a&gt;, and several others of varying levels of adoption, or ease-of-use (yes, while I love it for trivial cases, I'm looking at you &lt;a href="https://github.com/SwensenSoftware/unquote?tab=readme-ov-file" rel="noopener noreferrer"&gt;Unqoute&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;I decided to go with &lt;a href="https://github.com/haf/expecto" rel="noopener noreferrer"&gt;Expecto&lt;/a&gt;, One of F# community's most loved testing framework.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Expecto&lt;/code&gt; is truly a framework, not a library: it has a built-in test organizer, a test runner, and lastly, it has its own &lt;strong&gt;extensive&lt;/strong&gt; assertion library.&lt;br&gt;&lt;br&gt;
To top things off, its documentation is top-notch (though, sadly, not perfect - there are some parts no longer compatible with current APIs, so caveat emptor, and lean on the great F# community for help!)&lt;/p&gt;
&lt;h3&gt;
  
  
  Let me give you an example
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Expecto&lt;/code&gt; is an example-based testing framework.&lt;br&gt;&lt;br&gt;
What that means is that we run assertions on concrete instances: we run our functions-under-test with pre-defined inputs that we expect would yield a deterministic result. We then assert that the function's output matches the expected result.  &lt;/p&gt;

&lt;p&gt;Yes, that probably sounds like the testing you're used to, that you simply call  "testing", and what's with the "example-based" nonsense?&lt;br&gt;&lt;br&gt;
That will be explained later.&lt;/p&gt;
&lt;h3&gt;
  
  
  FYI
&lt;/h3&gt;

&lt;p&gt;There isn't much to say about &lt;code&gt;Expecto&lt;/code&gt; that its documentation, and the code examples in &lt;code&gt;test/server/FunParkTest&lt;/code&gt; folder don't cover.&lt;br&gt;&lt;br&gt;
Take a look.&lt;br&gt;&lt;br&gt;
Push comes to shove, example-based testing is mostly a “solved problem” in the domain of software engineering, what changes are the implementation details.&lt;/p&gt;

&lt;p&gt;I personally love &lt;code&gt;Expecto&lt;/code&gt;, as does most of the F# community, though I listed some alternatives you may want to tinker with to see what tickles your fancy.&lt;/p&gt;
&lt;h2&gt;
  
  
  Property of &amp;lt;entity under test&amp;gt;
&lt;/h2&gt;

&lt;p&gt;All the frameworks and libraries in the previous section were, as noted, example-based.&lt;/p&gt;

&lt;p&gt;Example-based testing is actually very well suited to Functional Programming languages due to those languages' attributes such as immutability-by-default, and pure functions having no side effects.&lt;/p&gt;

&lt;p&gt;But what if we could test our function on a deeper level? What if we could identify some core invariant(s) of our function, that are true no matter what input argument(s) we pass it?&lt;br&gt;&lt;br&gt;
This is what property-based testing is about.&lt;/p&gt;
&lt;h3&gt;
  
  
  Standing on the shoulders of giants
&lt;/h3&gt;

&lt;p&gt;F# guru, and all-round stand-up guy, Scott Wlaschin, has a &lt;a href="https://fsharpforfunandprofit.com/series/property-based-testing/" rel="noopener noreferrer"&gt;series on PBT&lt;/a&gt;, a true masterpiece, showing Scott's uncanny humor, as well as giving a thorough tour of PBT basics, and then some.&lt;br&gt;&lt;br&gt;
Check it out!&lt;/p&gt;
&lt;h3&gt;
  
  
  The hedgehog can never be buggered at all
&lt;/h3&gt;

&lt;p&gt;Current to this post publication date, F# has two well-established libraries for property-based testing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On the left corner, weighing in at v3.x, hailing from the OG PBT library, Haskell's &lt;code&gt;QuickCheck&lt;/code&gt;, of which it's a direct port, we have &lt;a href="https://fscheck.github.io/FsCheck/" rel="noopener noreferrer"&gt;FsCheck&lt;/a&gt; - F# premier PBT library, well established, battle-tested, community-driven, just solid.&lt;/li&gt;
&lt;li&gt;On the right corner, weighing in at v0.13.0, but &lt;strong&gt;very&lt;/strong&gt; actively developed by its maintainers, &lt;a href="https://hedgehogqa.github.io/fsharp-hedgehog/" rel="noopener noreferrer"&gt;Hedgehog&lt;/a&gt; - a multi-language PBT library (available also for Haskell and... sorry, I have to say a bad word, Scala). Not as well established as the incumbent, but still very welcome by the community, and an all-round excellent PBT library.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For my little reimplementation project, I decided to go with &lt;code&gt;Hedgehog&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;The reasoning? Twofold:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I just wanted to play with the "other" tool, the runner-up, full of promise.&lt;/li&gt;
&lt;li&gt;Unlike &lt;code&gt;FsCheck&lt;/code&gt; that lacks it, &lt;code&gt;Hedgehog&lt;/code&gt; boasts built-in shrinking!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Honey, I shrunk the test
&lt;/h3&gt;

&lt;p&gt;I'm sure the second bullet point made perfect sense, especially those who only learned of property-based testing just now, so we can move on.&lt;/p&gt;

&lt;p&gt;Just kidding.&lt;br&gt;&lt;br&gt;
To understand what shrinking is, let's take a &lt;strong&gt;hand-waving&lt;/strong&gt; detour of how property-based testing works.&lt;/p&gt;
&lt;h3&gt;
  
  
  How the property sausage is made
&lt;/h3&gt;

&lt;p&gt;At its core a PBT library generates random value according to the types of the inputs to the function-under-test, which it then "feeds" into said function and assert a certain invariant, &lt;strong&gt;the property&lt;/strong&gt;, is held for all those values.&lt;br&gt;&lt;br&gt;
(How those values are generated is a topic unto itself, you can &lt;a href="https://getcode.substack.com/p/property-based-testing-1-what-is" rel="noopener noreferrer"&gt;read about here&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;We, however, want to discuss what happens when the library generates a value that disproves the property.&lt;br&gt;&lt;br&gt;
True, the library could just return the offending value and call it a day, but, due to the input values being generated in a pseudo-random fashion, that value might be non-informative.&lt;/p&gt;

&lt;p&gt;As a silly, but illustrative example, think of a function that, due to a faulty implementation, expects all its inputs, which are strings, to contain the character &lt;code&gt;'a'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Most PBTs generate strings by appending a random draw of ASCII, or even UTF-8, characters to each other for a given length. It's not uncommon for a generated string to look like "tIg87%^K🔥FHkjがf".&lt;br&gt;&lt;br&gt;
Now suppose our library found this string is invalidating our property and returned it.&lt;br&gt;&lt;br&gt;
What &lt;strong&gt;useful&lt;/strong&gt; information did we get? &lt;strong&gt;Why&lt;/strong&gt; is that string invalid? Is it because of the emoji? The Kanji? Too long? Too short? Because it has two consecutive numbers (did you catch that?)&lt;br&gt;&lt;br&gt;
There are too many possibilities, and even when inspecting the (faulty) function's implementation, we might miss the issue because we'd be trying to spot an issue that matches our assumptions, that I wrote before, not looking for "must contain an 'a' character" flaw!&lt;/p&gt;

&lt;p&gt;Could the library do something to show us a failing input that just by looking at it we'd get that it's wrong and have an idea of how to fix it? For example, for our silly example function, the empty string, &lt;code&gt;""&lt;/code&gt;?&lt;br&gt;&lt;br&gt;
It would certainly help!&lt;/p&gt;

&lt;p&gt;The act of producing ever simpler examples until finding the most trivial example to invalidate the invariant is called "shrinking".&lt;/p&gt;
&lt;h3&gt;
  
  
  I like small values and I can not lie
&lt;/h3&gt;

&lt;p&gt;For simple types, e.g., primitive types, most PBT libraries know how to shrink towards a trivial example on their own: strings get iteratively shrunk into the empty string, numbers are shrunk from large ones to zero, lists are shrunk to the empty list, and so forth.&lt;/p&gt;

&lt;p&gt;But... what happens when the library is faced with a complex type? How does shrinking happen then?&lt;br&gt;&lt;br&gt;
Without knowledge on how to shrink, some PBT libraries would be forced to return the first offending value they find, not the most trivial one.&lt;/p&gt;

&lt;p&gt;The following example is taken from &lt;code&gt;Hedgehog&lt;/code&gt;'s &lt;a href="https://hedgehogqa.github.io/fsharp-hedgehog/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;&lt;br&gt;&lt;br&gt;
(I feel it's not explained well, &lt;strong&gt;and&lt;/strong&gt; the code snippets, for &lt;strong&gt;both&lt;/strong&gt; &lt;code&gt;FsCheck&lt;/code&gt; and &lt;code&gt;Hedgehog&lt;/code&gt; are incompatible with their respective current APIs.)&lt;/p&gt;

&lt;p&gt;Assume we have a type: &lt;code&gt;type MyVersion = MyVersion of maj: int * min: int * patch: int&lt;/code&gt; denoting a SemVer-comptiable version number. (Not using &lt;code&gt;Version&lt;/code&gt; as the type's name since there is a &lt;code&gt;System.Version&lt;/code&gt; already.)&lt;br&gt;&lt;br&gt;
We now have this, again silly, notion all SemVer-compatible numbers are reversible (not necessarily palindrome, e.g., "1.2.1", but also "240.35.240") so we define the function, &lt;code&gt;let revVer Ver(x, y, z) = Ver(z, y, x)&lt;/code&gt; (Yes, I called it &lt;code&gt;revVer&lt;/code&gt; on purpose, see if you get why?)&lt;/p&gt;

&lt;p&gt;Now, let's look at how &lt;code&gt;FsCheck&lt;/code&gt; will return on this obviously wrong property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Assuming FsCheck library is in context, of course&lt;/span&gt;
&lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="nc"&gt;FsCheck&lt;/span&gt;
&lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="nn"&gt;FsCheck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FSharp&lt;/span&gt; &lt;span class="c1"&gt;// New in v3.x of FsCheck&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;MyVersion&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MyVersion&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="n"&gt;maj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;verRev&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MyVersion&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pa&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MyVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pa&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ma&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="nn"&gt;Gen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choose&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Arb.Generator&amp;lt;'T&amp;gt; is not compatible with FsCheck v3.x, so we get creative. Choosing `byte` was purely to restrict the possible values to some reasonable size!&lt;/span&gt;
    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Gen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;three&lt;/span&gt;
    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Gen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pa&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;MyVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pa&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Arb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromGen&lt;/span&gt;

&lt;span class="nn"&gt;Prop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forAll&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="n"&gt;ver&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ver&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;verRev&lt;/span&gt; &lt;span class="n"&gt;ver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the output I got:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Falsifiable, after 1 test (0 shrinks) (7912673337469139209,2146181641386198071)
Last step was invoked with size of 2 and seed of (16035987728726137193,1406933410677738447):
Original:
MyVersion (63, 87, 87)
with exception:
System.Exception: Expected true, got false.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two takeaways from this very informative error message:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The counter-example: &lt;code&gt;MyVersion (63, 87, 87)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;No shrinking was done, because &lt;code&gt;FsCheck&lt;/code&gt; doesn't know how to shrink the custom type &lt;code&gt;MyVersion&lt;/code&gt;: "Falsifiable, after 1 test (0 shrinks)".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, before we discuss this any further, let's see how &lt;code&gt;Hedgehog&lt;/code&gt; deals with the same property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Assuming Hedgehog is in context&lt;/span&gt;
&lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="nc"&gt;Hedgehog&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;MyVersion&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MyVersion&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="n"&gt;maj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="n"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;revVer&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MyVersion&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pa&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MyVersion&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pa&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ma&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="nn"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;constantBounded&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Gen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;byte&lt;/span&gt;
    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Gen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Gen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tuple3&lt;/span&gt;
    &lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Gen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pa&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;MyVersion&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ma&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pa&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;property&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="n"&gt;vers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Gen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;linear&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;List&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forall&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="n"&gt;ver&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ver&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;verRev&lt;/span&gt; &lt;span class="n"&gt;ver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;vers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Property&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkBool&lt;/span&gt;
&lt;span class="p"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;printfn&lt;/span&gt; &lt;span class="s2"&gt;"%A"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: System.Exception: *** Failed! Falsifiable (after 1 test and 10 shrinks):
[MyVersion (0, 0, 1)]
This failure can be reproduced by running:
&amp;gt; Property.recheck "0_3673599252239236771_13184715031196647977_1010110110110110110110110110" &amp;lt;property&amp;gt;
at Hedgehog.ReportModule.tryRaise(Report report)
at Hedgehog.Property.checkBool(Property`1 g)
at &amp;lt;StartupCode$FSI_0010&amp;gt;.$FSI_0010.main@()
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can already see the difference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The counter-example is as minimal as can be: `MyVersion (0, 0,1).&lt;/li&gt;
&lt;li&gt;Shrinking occurred! &lt;code&gt;Hedgehog&lt;/code&gt; knows how to shrink even custom types, provided the types comprising the custom types are either primitive, or otherwise well-defined in our code: "Failed! Falsifiable (after 1 test and 10 shrinks)".&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  "You're not integral, to the project"
&lt;/h3&gt;

&lt;p&gt;Indeed, for a trivial example as our &lt;code&gt;Version&lt;/code&gt; type the difference between &lt;code&gt;Myersion (143, 2, 5)&lt;/code&gt; and &lt;code&gt;MyVersion (0, 0, 1)&lt;/code&gt; is, well, trivial. But it's not hard to think of properties where getting to the minimal counter-example would be tenfold easier to parse than a non-trivial one (e.g., a record type with 10 fields, each field itself a custom type.)&lt;/p&gt;

&lt;p&gt;The thing is, when dealing with custom types, &lt;code&gt;FsCheck&lt;/code&gt;, unless provided a shrinking strategy, for which it has a specific API, treats the custom type opaquely and atomically, i.e., as a single unit.&lt;br&gt;&lt;br&gt;
When a custom type fails a property, &lt;code&gt;FsCheck&lt;/code&gt; returns the offending non-trivial example, because it's the best it can do.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Hedgehog&lt;/code&gt;, on the other hand, and I'd be damned if I know how, that's why I'm only writing about it, not maintaining it, &lt;strong&gt;does&lt;/strong&gt; know how to pierce into a custom type and shrink its constituents - assuming they're primitive types, or otherwise well-defined in the code.&lt;/p&gt;

&lt;p&gt;This property, pun not intended but so very fitting, that some other PBT libraries in other programming languages also have, is called &lt;strong&gt;integral shrinking&lt;/strong&gt;, because, unlike &lt;code&gt;FsCheck&lt;/code&gt; that requires external shrinking strategies, frameworks with integral shrinking, know about shrinking on their own.&lt;/p&gt;

&lt;h3&gt;
  
  
  With great shrinking comes great responsibility
&lt;/h3&gt;

&lt;p&gt;Both &lt;code&gt;FsCheck&lt;/code&gt; and &lt;code&gt;Hedgehog&lt;/code&gt; boast great generators API, the APIs for creating (pseudo-) random inputs.&lt;br&gt;&lt;br&gt;
Actually, they're so great that for the most part you can use those APIs to generate inputs even for custom types without fail or problem.&lt;br&gt;&lt;br&gt;
But sometimes we need more, we need a generator so customized, wrapping over a type so complex, that we need to create our own generator.&lt;br&gt;&lt;br&gt;
Luckily, both libraries allow us that too. With ease!&lt;/p&gt;

&lt;p&gt;Check out &lt;code&gt;tests/server/FunParkHedgehog/HedgehogGenerators.fs&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
To grok the file, you need to know that &lt;a href="https://github.com/bchavez/Bogus" rel="noopener noreferrer"&gt;Bogus&lt;/a&gt; is an F# implementation of the great Ruby &lt;a href="https://github.com/faker-ruby/faker" rel="noopener noreferrer"&gt;Faker&lt;/a&gt; gem - a library for creating plausible-sounding values in a variety of domains.&lt;/p&gt;

&lt;p&gt;I wanted to use it to create human-sounding names for my &lt;code&gt;Patron&lt;/code&gt;s and &lt;code&gt;Ride&lt;/code&gt;s.&lt;br&gt;&lt;br&gt;
Could I have used &lt;code&gt;Bogus&lt;/code&gt; directly in the constructors for these types? Yes, of course.&lt;br&gt;&lt;br&gt;
But that would lose the (integral) shrinking the PBT libraries offer - &lt;code&gt;Bogus&lt;/code&gt; dishes out values of an (extensive) internal database. It has no notion of what shrinking is, and is not designed for that purpose.&lt;br&gt;&lt;br&gt;
Great idea in general, not good enough for &lt;strong&gt;my&lt;/strong&gt; tests. I need more!&lt;/p&gt;

&lt;p&gt;So, let's create a custom generator wrapping &lt;code&gt;Bogus&lt;/code&gt; - a generator that calls a &lt;code&gt;Bogus&lt;/code&gt; module, extracting a value from it, while retaining &lt;code&gt;Hedgehog&lt;/code&gt;'s ability to shrink the values if need be.&lt;/p&gt;

&lt;p&gt;First, we define a way to consistently get the same wrapper.&lt;br&gt;&lt;br&gt;
As you may have noted before, in the example code earlier, both libraries, upon failing, note the seed they used to create the random inputs.&lt;br&gt;&lt;br&gt;
Knowing the seed is essential for making sure we can re-run the tests with the same randomized inputs to verify the failing tests now pass.&lt;/p&gt;

&lt;p&gt;The implementation is rather straightforward, using &lt;code&gt;Bogus&lt;/code&gt;'s API for creating a seeded &lt;code&gt;Faker&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;The real "magic" happens in &lt;code&gt;genBogus&lt;/code&gt; (and its size-dependent cousin &lt;code&gt;genBogusSized&lt;/code&gt;, though I've never used a size-dependent generator in any of my tests, and I'm not sure why they even exist).&lt;/p&gt;

&lt;p&gt;In truth, &lt;code&gt;genBogus&lt;/code&gt; is not magical at all: it uses &lt;code&gt;Gen&lt;/code&gt; module's API to &lt;code&gt;map&lt;/code&gt; from the given seed &lt;strong&gt;generator&lt;/strong&gt;, a constraint imposed by &lt;code&gt;Gen.map&lt;/code&gt; input expectations, to a generator wrapper around a specific &lt;code&gt;Bogus&lt;/code&gt; API.&lt;br&gt;&lt;br&gt;
That really is all there is to it!&lt;/p&gt;

&lt;h3&gt;
  
  
  Tinker, tailored custom generators, solider, spy
&lt;/h3&gt;

&lt;p&gt;To see how it's used, look at &lt;code&gt;tests/server/FunParkHedgehog/RideTest.fs&lt;/code&gt;, specifically at the &lt;code&gt;name&lt;/code&gt; generator.&lt;/p&gt;

&lt;p&gt;We define &lt;code&gt;fakerName&lt;/code&gt; using our &lt;code&gt;genBogus&lt;/code&gt; which returns a generator wrapping over &lt;code&gt;Bogus.Faker.Name&lt;/code&gt; module, so, basically: &lt;code&gt;Gen&amp;lt;Bogus.Faker.Name&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you hover over &lt;code&gt;fakerName&lt;/code&gt; though, you'll see its type is &lt;code&gt;Bogus.Faker.Name&lt;/code&gt;, unwrapped.&lt;br&gt;&lt;br&gt;
That is because we defined it using &lt;code&gt;let!&lt;/code&gt; (i.e., "let-bang"), which bind the &lt;strong&gt;unwrapped&lt;/strong&gt; value.&lt;br&gt;&lt;br&gt;
(This non-magical "magic" is due to how the &lt;code&gt;gen&lt;/code&gt; builder, a computation expression, another unique F# feature, is implemented.&lt;br&gt;&lt;br&gt;
I advise on reading Scott Wlaschin's, who else?, &lt;a href="https://fsharpforfunandprofit.com/series/computation-expressions/" rel="noopener noreferrer"&gt;series on computation expression&lt;/a&gt; to understand these complex, but very rewarding features.)&lt;/p&gt;

&lt;p&gt;One more touch I added to my custom generator was &lt;strong&gt;custom shrinking&lt;/strong&gt;!&lt;br&gt;&lt;br&gt;
I didn't have to, as we now know, &lt;code&gt;Hedgehog&lt;/code&gt; has integral shrinking, but I wanted the shrinking to also use the &lt;code&gt;Faker.Name&lt;/code&gt; module, not resort to string shrinking, after all, &lt;code&gt;name&lt;/code&gt; is a string in the end, so, actually, I &lt;strong&gt;did&lt;/strong&gt; have to define a custom shrinker, but due to a business logic constraint, not a shortcoming of the library!&lt;/p&gt;

&lt;p&gt;The last nicety I added, strictly for flair and showing-off, I admit, was &lt;code&gt;tagSelector&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
Using F#'s reflection capabilities, nothing to do with &lt;code&gt;Hedgehog&lt;/code&gt;, it simply makes sure no tag is repeated in the tag list the &lt;strong&gt;&lt;code&gt;Ride&lt;/code&gt;&lt;/strong&gt; generator creates.&lt;/p&gt;

&lt;h3&gt;
  
  
  The worth of a testing library
&lt;/h3&gt;

&lt;p&gt;One question to contemplate is "is this integral shrinking worth it?"&lt;br&gt;&lt;br&gt;
That's not a trivial question! While lacking in that department, at least currently, who knows what &lt;code&gt;FsCheck&lt;/code&gt;'s maintainers have in store, it does have a slew of other helper functions that make it an extraordinary testing framework: integral logging, custom distribution of inputs generated, and much more.&lt;/p&gt;

&lt;p&gt;It may well be that &lt;code&gt;Hedgehog&lt;/code&gt; has all those features too, but its documentation is &lt;strong&gt;very&lt;/strong&gt; lacking, and if it does have those features... I don't know of them, or how to invoke them.&lt;br&gt;&lt;br&gt;
(Note that &lt;code&gt;FsCheck&lt;/code&gt;'s documentation is also lacking, and actually the official docs, on the website, are still those of v2.x, where the library is now v3.x!)&lt;/p&gt;

&lt;p&gt;The answer to that question is left to you, dear reader, and your needs, and abilities, per project.&lt;/p&gt;

&lt;p&gt;I wanted to try &lt;code&gt;Hedgehog&lt;/code&gt;'s internal shrinking, and this is, after all, a toy project I do for fun... no one's going to fault me for writing wrong tests, not having a 100% coverage, and so forth.&lt;br&gt;&lt;br&gt;
In real-life, production, projects you need to weigh &lt;strong&gt;all&lt;/strong&gt; constraints you'd be facing and make an informed decision.&lt;br&gt;&lt;br&gt;
Hopefully this post helps with that.&lt;/p&gt;

&lt;h2&gt;
  
  
  And now, the time has come
&lt;/h2&gt;

&lt;p&gt;This has been an even longer, bumpier, ride, ha-ha, get it, &lt;code&gt;Ride&lt;/code&gt;, tough crowd, than the previous one, because the subject of testing, and property-based testing in particular is vast, to say the least.&lt;/p&gt;

&lt;p&gt;I hope you &lt;strong&gt;did&lt;/strong&gt; get at least some idea of how we test in F#, and of what PBT are, why they're useful, and a glimpse of how to actually use them.&lt;/p&gt;

&lt;p&gt;As with the previous post, I put useful links throughout the text, to folks much more proficient than myself in F# (and the skill of writing succinctly, yet precisely. I know my own faults, yes.)&lt;br&gt;
You'd be wise to follow up on these links, especially the ones to any of Scott Wlaschin's posts!&lt;/p&gt;

&lt;h2&gt;
  
  
  Adieu
&lt;/h2&gt;

&lt;p&gt;The next two chapters in the original material are about custom equality and comparison.&lt;/p&gt;

&lt;p&gt;Elixir doesn't have any built-in facilities to deal with either.&lt;br&gt;&lt;br&gt;
F# on the other hand does. For both, custom equality, &lt;strong&gt;and&lt;/strong&gt; custom comparison.&lt;/p&gt;

&lt;p&gt;But it's going to take me a while to read the chapters in Elixir, code them in F#, and write voluminous posts about it. 😁&lt;/p&gt;

&lt;p&gt;So, for the time being, this is goodbye.&lt;br&gt;&lt;br&gt;
Hope you enjoyed these two posts, that you learned something, that something clicked for you.&lt;/p&gt;

&lt;p&gt;And hey, maybe let's call it "till next time".&lt;br&gt;&lt;br&gt;
It's going to take a while, but I promise you, like Arnie... I'll be back!&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>fsharp</category>
      <category>testing</category>
    </item>
    <item>
      <title>Domain modeling, Units-of-Measure, and Property-based testing, oh my</title>
      <dc:creator>O.F.K.</dc:creator>
      <pubDate>Tue, 09 Sep 2025 11:14:54 +0000</pubDate>
      <link>https://dev.to/omanfk/domain-modeling-units-of-measure-and-property-based-testing-oh-my-502o</link>
      <guid>https://dev.to/omanfk/domain-modeling-units-of-measure-and-property-based-testing-oh-my-502o</guid>
      <description>&lt;p&gt;When I learned about "Advanced Functional Programming with Elixir" by Joseph Koski (&lt;a href="https://pragprog.com/titles/jkelixir/advanced-functional-programming-with-elixir/" rel="noopener noreferrer"&gt;PragProg publishing&lt;/a&gt;) - detailing building a miniature theme park application (a favorite gaming genre of mine) - I rushed to buy it.&lt;br&gt;&lt;br&gt;
After all, Elixir is a wonderful FP language I enjoyed coding in the past.&lt;/p&gt;

&lt;p&gt;But there's a twist - I'm reimplementing the system in F#, using everything I know about F# to make the domain safer and correct!&lt;br&gt;&lt;br&gt;
For all my love for Elixir, F# is much better at leading the developer into the "pit of success."&lt;/p&gt;

&lt;p&gt;As with all coding posts, I suggest you &lt;a href="https://github.com/OmanF/funpark_advanced_fp_with_fsharp" rel="noopener noreferrer"&gt;checkout the repo&lt;/a&gt; and take a look at the code while reading.&lt;/p&gt;
&lt;h2&gt;
  
  
  Grokking our domain (covering original book's chapter 1)
&lt;/h2&gt;

&lt;p&gt;As is customary, and wise, we start the journey with the domain entities: &lt;code&gt;Ride&lt;/code&gt;, &lt;code&gt;FreePass&lt;/code&gt;, and &lt;code&gt;Patrons&lt;/code&gt; (which are the park guests, as per the ubiquitous language for our domain).&lt;/p&gt;

&lt;p&gt;The book starts with &lt;code&gt;Ride&lt;/code&gt;s.&lt;br&gt;&lt;br&gt;
The author immediately shows good software design by declaring it as a structure, an Elixir construct that takes specific shape, but only allowing instantiation via a constructor.&lt;br&gt;&lt;br&gt;
I &lt;strong&gt;was&lt;/strong&gt; surprised that though Elixir does feature private functions, the author made the constructor public, defeating the purpose of only allowing instansiation via it.&lt;/p&gt;

&lt;p&gt;In the F# implementation, I made the constructors private - no ad-hoc entity creation anywhere in the project by "rogue" code, no surprises!&lt;br&gt;&lt;br&gt;
But, with great control comes great compromises and technical challenges as we shall see momentairly.&lt;/p&gt;

&lt;p&gt;Right off the bat, as I was modelling &lt;code&gt;Ride&lt;/code&gt; in F#, some key differences between Elixir and F# stood out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Elixir has succinct null-string verification but requires pattern-matching for empty/all-whitespace checks. F# actually doesn't have even that functionality built-in, but it's easy to implement by hand.&lt;/li&gt;
&lt;li&gt;No built-in UUID generation in Elixir, forcing integer IDs. F# wins this one with robust GUID/UUID generation tools right in the standard library.&lt;/li&gt;
&lt;li&gt;Erlang, on top of which Elixir is running, has symbols for strictly positive integers (which in math are called Naturals). F# doesn't ,but, again, it's easy to implement.&lt;/li&gt;
&lt;li&gt;Elixir supports passing default values to functions while F# doesn't.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As an aside, I favor F#'s approach: call functions with all required arguments, or let it fail.&lt;br&gt;&lt;br&gt;
I &lt;strong&gt;did&lt;/strong&gt; however implement defaults, for the challenge.&lt;br&gt;&lt;br&gt;
(Keep in mind, default values grant type-, and runtime safety, allowing an object to be instasiated despite missing arguments, but might not make business sense!&lt;br&gt;&lt;br&gt;
Programming is, as is well known, an exercise in compromise.)&lt;/p&gt;

&lt;p&gt;To make constructors private in F#, there are two approaches: FP-leaning and OO-leaning: see &lt;a href="https://fsharpforfunandprofit.com/posts/designing-with-types-intro/" rel="noopener noreferrer"&gt;the series on constrained types&lt;/a&gt; by (the great) Scott Wlaschin.&lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;Ride&lt;/code&gt; type, I chose the FP-leaning approach purely for stylistic reasons.&lt;br&gt;&lt;br&gt;
Programming is also an exercise in style. 😜&lt;/p&gt;

&lt;p&gt;To start an FP-leaning private type/constructor combo we first need to define the private type. The logic is impeccable, right?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// All preceding code elided&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Ride&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Guid&lt;/span&gt;
              &lt;span class="nc"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
              &lt;span class="nc"&gt;MinAge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;yr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="nc"&gt;MinHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;cm&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="nc"&gt;WaitTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="nc"&gt;Online&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;RideStatus&lt;/span&gt;
              &lt;span class="nc"&gt;Tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;RideTags&lt;/span&gt; &lt;span class="kt"&gt;list&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Can't compare apples to Lamborghinis
&lt;/h3&gt;

&lt;p&gt;We can already see a feature of F#, not available out-of-the-box in any other programming language I'm aware of: Units-of-Measure (in short: UoM).&lt;/p&gt;

&lt;p&gt;Notice those &lt;code&gt;&amp;lt;yr&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;cm&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;s&amp;gt;&lt;/code&gt;? Those are tags, units, that qualify the numerical, integer, quantity they're attached to.&lt;/p&gt;

&lt;p&gt;.Net has a few of them, the SI units, predefined, e.g., &lt;code&gt;&amp;lt;s&amp;gt;&lt;/code&gt; which is the SI unit &lt;code&gt;second&lt;/code&gt;. The others I had to qualify myself (look in &lt;code&gt;Shared.fs&lt;/code&gt; file in the &lt;code&gt;Shared&lt;/code&gt; folder to see how).&lt;/p&gt;

&lt;p&gt;Thing is, much like a disciminated union, also known as &lt;strong&gt;tagged&lt;/strong&gt; union, since each case acts as a tag, marking its corresponding data, UoM are light-weight tags too: an &lt;code&gt;int&amp;lt;yr&amp;gt;&lt;/code&gt; is type incompatible with &lt;code&gt;int&amp;lt;s&amp;gt;&lt;/code&gt;: if I were to do &lt;code&gt;5&amp;lt;yr&amp;gt; = 5&amp;lt;s&amp;gt;&lt;/code&gt;, the compiler would throw a type error!&lt;/p&gt;

&lt;h3&gt;
  
  
  Can we have some privacy, please
&lt;/h3&gt;

&lt;p&gt;Now, this code only defines the &lt;strong&gt;private&lt;/strong&gt; type, but not the constructor I talked about.&lt;/p&gt;

&lt;p&gt;The point to understand is that by qualifying the type as &lt;code&gt;private&lt;/code&gt;, the compiler will now allow only code in the type's enclosing module and/or namespace to access the type.&lt;/p&gt;

&lt;p&gt;The way to create a constructor for the private type, which is public, is to create a sub-module, F# supports nesting modules as deep as you like, in the "main" module that will have a function that creates an instance, in the OO meaning of the word, F# types get compiled down to classes, of that type.&lt;br&gt;&lt;br&gt;
It's convention to name the sub-module after the type it creats, so I now declared &lt;code&gt;module Ride&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now came the time for another contemplatation: how to pass the arguments, six of them (&lt;code&gt;Name&lt;/code&gt;, &lt;code&gt;MinAge&lt;/code&gt;, &lt;code&gt;MinHeight&lt;/code&gt;, &lt;code&gt;WaitTime&lt;/code&gt;, &lt;code&gt;Online&lt;/code&gt;, &lt;code&gt;Tags&lt;/code&gt;) to the constructor: either passing them as one tupled argument, or six curried ones, meant all of them would be positional: their place in the order of passed arguments will dictate what argument they are: the first is &lt;code&gt;Name&lt;/code&gt;, last is &lt;code&gt;Tags&lt;/code&gt;... for an arbitrary function that has that exact parameter list!&lt;/p&gt;

&lt;p&gt;That would work, obviously, but given there are six such parameters, this risks mixing them up causing errors.&lt;br&gt;&lt;br&gt;
True, in this specific case, most of the arguments being tagged by UoM, any such mixups would be caught by the compiler as errors, but I'm lazy... re-typing a six argument list by hand over does &lt;strong&gt;not&lt;/strong&gt; sound appealing!&lt;/p&gt;

&lt;p&gt;I figured there must be a better way to pass the parameters to the function - and there is!&lt;/p&gt;

&lt;p&gt;Luckily for me, the ID properity of each of the entities is generated via the &lt;code&gt;Systenm.Guid.NewGuid()&lt;/code&gt; method.&lt;br&gt;&lt;br&gt;
With that in mind I created a new type to provide the constructor function its arguments in a &lt;strong&gt;named&lt;/strong&gt; fashion.&lt;/p&gt;

&lt;p&gt;While I first hoped to pass an &lt;strong&gt;annonymous&lt;/strong&gt; record to the constructor, e.g. &lt;code&gt;{|Name = "myName"; MinAge = 8&amp;lt;yr&amp;gt;...|}&lt;/code&gt;, functions in F# do &lt;strong&gt;not&lt;/strong&gt; allow annonymous recrods as parameters, so a full-fledged type was in order.&lt;/p&gt;

&lt;p&gt;What I came up was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;    &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;Ride&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;RideConstructor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ContentfulString&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;
              &lt;span class="nc"&gt;MinAge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Natural&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;yr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;
              &lt;span class="nc"&gt;MinHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Natural&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;cm&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;
              &lt;span class="nc"&gt;WaitTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Natural&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;
              &lt;span class="nc"&gt;Online&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;RideStatus&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;
              &lt;span class="nc"&gt;Tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;RideTags&lt;/span&gt; &lt;span class="kt"&gt;list&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  I'm gonna have my own types with private constructors and validations
&lt;/h3&gt;

&lt;p&gt;I guess I now have some 'xplaining to do with regards to what are &lt;code&gt;ConteftfulString&lt;/code&gt; and &lt;code&gt;Natural&lt;/code&gt; which surely aren't built-in F# types.&lt;/p&gt;

&lt;p&gt;I want my &lt;code&gt;Ride&lt;/code&gt;s to have meaningful names, e.g. not the empty string, nor an all whitespace name, and they definitely must not be null.&lt;br&gt;&lt;br&gt;
Same for the numerical value denoting age, heigt and wait time: neither of them can be a negative value, and actually, neither can be zero either! (Well, &lt;code&gt;WaitTime&lt;/code&gt; can, in theory, denoting an offline ride, as we shall see in an upcoming post about testing the domain!)&lt;/p&gt;

&lt;p&gt;Both these types are also constrained types: &lt;code&gt;private&lt;/code&gt; with constructor and extractor functions and some logic to constrain the type instances' creation, to only allow values that are valid for the domain.&lt;/p&gt;

&lt;p&gt;And yet, each of these types is also slightly different the &lt;code&gt;Ride&lt;/code&gt; too.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;ContentfulString&lt;/code&gt; I used the OO-leaning construction of constrained types, simply because the OO-leaning way allows to attach the extractor to the type as a member, later allowing value extraction via the syntax &lt;code&gt;aContentfulString.Value&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;ContentfulString&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;member&lt;/span&gt; &lt;span class="nc"&gt;Create&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;IsNullOrWhiteSpace&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                &lt;span class="nc"&gt;None&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;
                &lt;span class="nc"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ContentfulString&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;member&lt;/span&gt; &lt;span class="o"&gt;_.&lt;/span&gt;&lt;span class="nc"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For &lt;code&gt;Natural&lt;/code&gt;, since it would be denoting a tagged UoM quantity, its definition needs to support that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="nc"&gt;Natural&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;[&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Measure&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;]&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Natural&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;Natural&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;_&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="nc"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Natural&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nc"&gt;None&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Natural&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Failure is not an option (but in this case, it is)
&lt;/h3&gt;

&lt;p&gt;Going back to the constructor type, you may be wondering, "but why are &lt;strong&gt;all&lt;/strong&gt; the inputs to this record &lt;code&gt;option&lt;/code&gt;al"?&lt;br&gt;&lt;br&gt;
The answer has to do with the &lt;code&gt;Natural&lt;/code&gt; and &lt;code&gt;ContenfulString&lt;/code&gt; constructors.&lt;/p&gt;

&lt;p&gt;If you look at the constructors of these two types you'll notice both output an &lt;code&gt;option&lt;/code&gt;al: &lt;code&gt;Some&amp;lt;'T&amp;gt;&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
The reason is that when constructing those types the input might be invalid, e.g., a negative number for the &lt;code&gt;Natural&lt;/code&gt; constructor.&lt;/p&gt;

&lt;p&gt;In that case, what should the constructor do? One way to go would be to raise an exception, but that's a very dramatic option, and also not very testable (the test would throw on any negative-path test, polluting the entire test suite's results!)&lt;br&gt;&lt;br&gt;
The better way, the F# idiomatic way, is to use the &lt;code&gt;Option&lt;/code&gt; type: when an invalid input is given return &lt;code&gt;None&lt;/code&gt;, otherwise, as noted earlier, return &lt;code&gt;Some&amp;lt;'T&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;"Sure", I hear you say, "that makes sense for the name and the numerical quantities that use your custom, constrained, types. But what about &lt;code&gt;Online&lt;/code&gt; and &lt;code&gt;Tags&lt;/code&gt;? Those are custom types, but they don't have constrained constructors, so, what's the deal?!"&lt;/p&gt;
&lt;h2&gt;
  
  
  Do, or do not, there is no default
&lt;/h2&gt;

&lt;p&gt;The answer to &lt;strong&gt;that&lt;/strong&gt; question is that by now I was sure I had the &lt;code&gt;Ride&lt;/code&gt; type figured out and it was time to create the constructor function.&lt;/p&gt;

&lt;p&gt;As mentioned earlier, though I object, personally, to the notion of default values, I decided to implement them in this domain for the sheer challenge of it.&lt;br&gt;&lt;br&gt;
I also mentioned I created the &lt;code&gt;RideConstructor&lt;/code&gt; type to be able to pass named arguments to the function.&lt;/p&gt;

&lt;p&gt;What this amounts to is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Within the `Ride` module&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt;
    &lt;span class="o"&gt;({&lt;/span&gt; &lt;span class="nc"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
       &lt;span class="nc"&gt;MinAge&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;minAge&lt;/span&gt;
       &lt;span class="nc"&gt;MinHeight&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;minHeight&lt;/span&gt;
       &lt;span class="nc"&gt;WaitTime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;waitTime&lt;/span&gt;
       &lt;span class="nc"&gt;Online&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;online&lt;/span&gt;
       &lt;span class="nc"&gt;Tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;}:&lt;/span&gt; &lt;span class="nc"&gt;RideConstructor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NewGuid&lt;/span&gt;&lt;span class="bp"&gt;()&lt;/span&gt;
      &lt;span class="nc"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defaultArg&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ContentfulString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="s2"&gt;"Generic ride!"&lt;/span&gt;
      &lt;span class="nc"&gt;MinAge&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defaultArg&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="nn"&gt;Natural&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;minAge&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;yr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nc"&gt;MinHeight&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defaultArg&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="nn"&gt;Natural&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;minHeight&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;cm&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nc"&gt;WaitTime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;online&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Some&lt;/span&gt; &lt;span class="nc"&gt;Offline&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
            &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="n"&gt;defaultArg&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="nn"&gt;Natural&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;waitTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nc"&gt;Online&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;defaultArg&lt;/span&gt; &lt;span class="n"&gt;online&lt;/span&gt; &lt;span class="nc"&gt;Online&lt;/span&gt;
      &lt;span class="nc"&gt;Tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;List&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;distinct&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aside: I didn't have to supply the input's type, &lt;code&gt;RideConstructor&lt;/code&gt;, F#'s type inference engine would have infered it by its own, but I decided to add it as a hint for future readers (i.e., &lt;strong&gt;me&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;Now it should be apparent why I made all the inputs to the constructor optional... as I said before, F# doesn't support default value to function's arguments at the function's call site.&lt;br&gt;&lt;br&gt;
What F# &lt;strong&gt;does&lt;/strong&gt; have is the &lt;code&gt;defaultArg&lt;/code&gt; method, as part of its core library.&lt;br&gt;&lt;br&gt;
The type signature for this method is &lt;code&gt;option&amp;lt;'T&amp;gt; -&amp;gt; 'T -&amp;gt; 'T&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's deconstruct that: the first argument is a generic optional value, i.e., it can be of &lt;strong&gt;any&lt;/strong&gt; inner type.&lt;br&gt;&lt;br&gt;
The second argument is a &lt;strong&gt;concrete&lt;/strong&gt; value of the same type.&lt;br&gt;&lt;br&gt;
The outcome is a concrete value of the same type, too.&lt;/p&gt;

&lt;p&gt;What that means is that, in essence, &lt;code&gt;defaultArg&lt;/code&gt; takes an optional, and a concrete value: if the optional exist, i.e., its a &lt;code&gt;Some&amp;lt;'T&amp;gt;&lt;/code&gt;, we get back the inner value of the &lt;code&gt;Some&lt;/code&gt;. If, however, we pass a &lt;code&gt;None&lt;/code&gt;, the method's output is the concrete value we passed as its second argument: the default value!&lt;/p&gt;

&lt;p&gt;I didn't say it's &lt;em&gt;hard&lt;/em&gt; creating default values in F#, just that it's not possible at function's call site. 😉&lt;/p&gt;

&lt;p&gt;Since I wanted consistency in my parameters, and allowing default values to all parameters, like in the original book, I set &lt;code&gt;Online&lt;/code&gt; and &lt;code&gt;Tags&lt;/code&gt; as &lt;code&gt;option&lt;/code&gt;al as well, and used &lt;code&gt;defaultArg&lt;/code&gt; on them, too.&lt;/p&gt;

&lt;p&gt;BTW, as I mentioned earlier, default values provide type safety, not business logic safety!&lt;br&gt;&lt;br&gt;
Consider the default name I assign: "Generic ride!". In the unfortunate event our ride constructor function (not yet implemented) is failing we'd have quite a lot of "Generic ride!"s.&lt;br&gt;&lt;br&gt;
Does it make sense? Nope! But that's what you get when you opt-out of failing fast!&lt;/p&gt;
&lt;h3&gt;
  
  
  It's the little touches that count
&lt;/h3&gt;

&lt;p&gt;Maybe you also noticed that I employ &lt;code&gt;List.distinct&lt;/code&gt; on the tags list, or the algorithm for creating the waiting time?&lt;br&gt;&lt;br&gt;
Neither of these is in the original book, but I like it.&lt;/p&gt;

&lt;p&gt;The first ensures there are no duplicate tags in the tag list provided to the constructor, no sense in that after all, and the second makes sure an offline ride has a 0 wait time... no one is waiting on an unoperational ride!&lt;/p&gt;
&lt;h3&gt;
  
  
  The private constructor conundrum
&lt;/h3&gt;

&lt;p&gt;At this point I was sure I'm done with the &lt;code&gt;Ride&lt;/code&gt; type and can move on to the others. I used F#'s REPL (AKA &lt;code&gt;fsi&lt;/code&gt;) to create a few &lt;code&gt;Ride&lt;/code&gt;s and things seemed to work fine.&lt;br&gt;&lt;br&gt;
Or did they?&lt;/p&gt;

&lt;p&gt;Everything &lt;strong&gt;was&lt;/strong&gt; fine... until I needed to access an instance's fields in tests, at which point all hell broke loose!&lt;br&gt;&lt;br&gt;
It's not so much a mistake, as a limitation imposed by the fact that for a private type, only the module holding its defintion can access its fields!&lt;br&gt;&lt;br&gt;
Even an extractor function wouldn't work: its return type would be &lt;code&gt;Ride -&amp;gt; Ride&lt;/code&gt;, but &lt;code&gt;Ride&lt;/code&gt; is private!&lt;/p&gt;
&lt;h3&gt;
  
  
  Twins - like in the Schwarzenegger/De-Vito movie
&lt;/h3&gt;

&lt;p&gt;This may surprise you, as in the examples from Scott Wlaschin's blog posted earlier, the extractor works fine.&lt;/p&gt;

&lt;p&gt;The difference stems from Scott's example constrained types being "boxes" for inner values like &lt;code&gt;String50 str&lt;/code&gt; - when extracting, we want the &lt;code&gt;str&lt;/code&gt; content, not the &lt;code&gt;String50&lt;/code&gt; wrapper.&lt;/p&gt;

&lt;p&gt;Our domain types, on the other hand, are the &lt;strong&gt;entirety&lt;/strong&gt; of what the constructor produces. Extracting a &lt;code&gt;Ride&lt;/code&gt; simply returns another &lt;strong&gt;private&lt;/strong&gt; &lt;code&gt;Ride&lt;/code&gt; which &lt;strong&gt;is&lt;/strong&gt; what we need back!&lt;/p&gt;

&lt;p&gt;The solution turned out to be intuitive but, admittedly, cumbersome: creating a public clone type, &lt;code&gt;RideView&lt;/code&gt;, and setting its fields to the original instance's values.&lt;br&gt;&lt;br&gt;
Programming is, it turns out, an exercise in creative workarounds. 🤯&lt;/p&gt;
&lt;h3&gt;
  
  
  The closer - Like Brenda Leigh Johnson
&lt;/h3&gt;

&lt;p&gt;It's now time to bring the &lt;code&gt;Ride&lt;/code&gt; type to conclusion.&lt;/p&gt;

&lt;p&gt;With the &lt;code&gt;RideView&lt;/code&gt; type being public, I could now write an extractor function that returns a type whose members I could access on demand, which is the final part missing before I can test my implementation for correctness.&lt;/p&gt;

&lt;p&gt;In my &lt;code&gt;Rides.fs&lt;/code&gt; file I made a design decision to put &lt;code&gt;RideView&lt;/code&gt; &lt;strong&gt;after&lt;/strong&gt; the &lt;code&gt;Ride&lt;/code&gt; type.&lt;br&gt;&lt;br&gt;
This has significance as F#'s compiler infers types that have the same shape in order from top to bottom, so when referring to types of the shape of &lt;code&gt;Ride&lt;/code&gt;/&lt;code&gt;RideView&lt;/code&gt;, unless explicitly annotated, inference will &lt;strong&gt;always&lt;/strong&gt; infer &lt;code&gt;RideView&lt;/code&gt;, which is not what we want!&lt;/p&gt;

&lt;p&gt;For instace, our &lt;code&gt;create&lt;/code&gt; function now has to be annotated on its &lt;strong&gt;return&lt;/strong&gt; type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;create&lt;/span&gt;
            &lt;span class="o"&gt;({&lt;/span&gt; &lt;span class="nc"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
               &lt;span class="nc"&gt;MinAge&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;minAge&lt;/span&gt;
               &lt;span class="nc"&gt;MinHeight&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;minHeight&lt;/span&gt;
               &lt;span class="nc"&gt;WaitTime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;waitTime&lt;/span&gt;
               &lt;span class="nc"&gt;Online&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;online&lt;/span&gt;
               &lt;span class="nc"&gt;Tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;}:&lt;/span&gt; &lt;span class="nc"&gt;RideConstructor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;// Notice the return type annotation&lt;/span&gt;
            &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Ride&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason is when creating I do want a &lt;code&gt;Ride&lt;/code&gt;, not &lt;code&gt;RideView&lt;/code&gt;, but without annotation as discussed, the type would have been infered incorrectly.&lt;/p&gt;

&lt;p&gt;Lastly, we finally are ready to create the extractor function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ride&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Ride&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;RideView&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ride&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Id&lt;/span&gt;
              &lt;span class="nc"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ride&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Name&lt;/span&gt;
              &lt;span class="nc"&gt;MinAge&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ride&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MinAge&lt;/span&gt;
              &lt;span class="nc"&gt;MinHeight&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ride&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MinHeight&lt;/span&gt;
              &lt;span class="nc"&gt;WaitTime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ride&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;WaitTime&lt;/span&gt;
              &lt;span class="nc"&gt;Online&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ride&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Online&lt;/span&gt;
              &lt;span class="nc"&gt;Tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ride&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Tags&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time I didn't &lt;em&gt;have&lt;/em&gt; to annotate the output, inference would have worked just fine. I did, however, have to annotate the &lt;strong&gt;input&lt;/strong&gt; to let the compiler now I'm only willing to accept &lt;code&gt;Ride&lt;/code&gt;s to the extractor function... there's no sense in extracting a &lt;code&gt;RideView&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The body of the function is pretty self-explantory: create a record of the public &lt;code&gt;RideView&lt;/code&gt; type, a public clone of &lt;code&gt;Ride&lt;/code&gt;, and assign each of its members the corresponding value from the private &lt;code&gt;Ride&lt;/code&gt;... a 1-to-1 clone of the input, but public, with accessible members that would be testable.&lt;/p&gt;

&lt;h3&gt;
  
  
  All good things come to an end
&lt;/h3&gt;

&lt;p&gt;With this, the &lt;code&gt;Rides&lt;/code&gt; module is complete: we have a private constructor to only allow &lt;code&gt;Ride&lt;/code&gt;s to be constructed by a trusted function, and a public extractor to allow testing.&lt;/p&gt;

&lt;p&gt;It tooks some compromises and some work-arounds, but it is now done.&lt;br&gt;
Mission - accomplished!&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;FreePass&lt;/code&gt; and &lt;code&gt;Patrons&lt;/code&gt; types follow the exact same logic, with the same constrained types (actually &lt;code&gt;FreePass&lt;/code&gt; takes valid &lt;code&gt;Ride&lt;/code&gt;s and .Net's native &lt;code&gt;DateTime&lt;/code&gt;... the constructor logic is straight-forward once you followed this post).&lt;/p&gt;

&lt;p&gt;And so we finished our initial domain modelling according to what we know about the domain thus far.&lt;/p&gt;

&lt;p&gt;Now remains the "little" matter of testing to make sure my implementations actually got the job right.&lt;br&gt;&lt;br&gt;
But that's for another post... 😝&lt;/p&gt;

&lt;p&gt;Thanks for reading all this scroll of a post, hope you enjoyed, maybe even learned something new, and, of course, comments are welcome, especially if they're nice. 🤣&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>fsharp</category>
    </item>
  </channel>
</rss>
