<?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: John</title>
    <description>The latest articles on DEV Community by John (@johnridesabike).</description>
    <link>https://dev.to/johnridesabike</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%2F155021%2F4e197016-2050-44e5-8006-d88aa7704f65.png</url>
      <title>DEV Community: John</title>
      <link>https://dev.to/johnridesabike</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/johnridesabike"/>
    <language>en</language>
    <item>
      <title>Building Coronate: type-driven development and scorekeeping</title>
      <dc:creator>John</dc:creator>
      <pubDate>Wed, 01 Jul 2020 14:31:16 +0000</pubDate>
      <link>https://dev.to/johnridesabike/building-coronate-type-driven-development-and-scorekeeping-14ib</link>
      <guid>https://dev.to/johnridesabike/building-coronate-type-driven-development-and-scorekeeping-14ib</guid>
      <description>&lt;p&gt;&lt;em&gt;May 2021 update: I wrote this article when Coronate was using ReasonML and ReasonReact. Those projects have since been replaced with &lt;a href="https://rescript-lang.org/" rel="noopener noreferrer"&gt;ReScript&lt;/a&gt;. ReScript provides most of the same features, but the naming and syntax is slightly different.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Recently, I was fixing something with my &lt;a href="https://github.com/johnridesabike/coronate" rel="noopener noreferrer"&gt;Coronate chess tournament manager app&lt;/a&gt;, and I had trouble debugging some faulty scorekeeping code. I opted for a type-driven refactor which ended up erasing the bugs and making the code more robust and easier to manage. This post explains how I did it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scorekeeping: the basics
&lt;/h2&gt;

&lt;p&gt;Scores are one of those things that seem intuitive, but get complicated once you  try implementing them in an app. In Coronate, players earn one point for winning a match, earn zero points for losing a match, and earn a half-point if the match was a draw. Additionally, there are some tie-break calculations which use negative points.&lt;/p&gt;

&lt;p&gt;Different modules in Coronate use scores for different reasons. The most basic is the scoreboard component. It adds up the results of each match for each player so it can rank the overall winners. The same data is also used to automatically pair players in a new round. Other components use the scores to display the match history for each player.&lt;/p&gt;

&lt;p&gt;Depending on the language you’re using, and your preferred mode of solving problems, you may have different ideas on how to implement this. When I originally wrote the code in JavaScript, all scores were plain &lt;code&gt;number&lt;/code&gt;s. Match results were stored as &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;0&lt;/code&gt;, or &lt;code&gt;0.5&lt;/code&gt; for each player, each respectively translating to a win, loss, or draw. The scoreboard just needed to compute the sum of these to determine the overall rankings. This code remained mostly unchanged when I migrated to &lt;a href="https://reasonml.org" rel="noopener noreferrer"&gt;Reason&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But from a type-driven perspective, we can refine this code. With individual matches, we only need a few specific values. For the overall rankings, we need floating-point numbers. A value like &lt;code&gt;3.5&lt;/code&gt; makes sense for the scoreboard, but it makes no sense as the result of a match.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem I faced
&lt;/h2&gt;

&lt;p&gt;A key part of the scorekeeping involves computing each player’s score against each other player. For example, say we need to know how many times Laura won or lost against Bob. To implement this, I created a &lt;code&gt;Belt.Map&lt;/code&gt; for each player called &lt;code&gt;opponentResults&lt;/code&gt;. Suppose that Laura had won two matches and lost one match against Bob. The code will take Laura’s &lt;code&gt;opponentResults&lt;/code&gt; map, look up the key for Bob, and it will retrieve the number &lt;code&gt;2&lt;/code&gt;, indicating that’s her score against him. If it takes Bob’s &lt;code&gt;opponentResults&lt;/code&gt; map and looks up Laura, it will see &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This seems simple enough, but it caused me trouble later.&lt;/p&gt;

&lt;p&gt;At some point afterwards, I needed to write a component that would display the match history of each player. I looked at the scorekeeping data I was already computing, and I had an idea: I could use the &lt;code&gt;opponentResults&lt;/code&gt; map for this purpose as well. That makes sense, right? Just run a reducer on the map to build an array of matches and their results.&lt;/p&gt;

&lt;p&gt;I used code that looked something like this (written in Reason):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;opponentName&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="s2"&gt;" - Lost"&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;0&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;opponentName&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="s2"&gt;" - Won"&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;opponentName&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="s2"&gt;" - Draw"&lt;/span&gt; &lt;span class="cm"&gt;/* a catch-all case so as not to raise an exception */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may have already noticed the problem. The map of opponent results represents the &lt;em&gt;sum&lt;/em&gt; of scores against each player, &lt;em&gt;not individual match results&lt;/em&gt;! In our imaginary scenario with Laura and Bob, it would list Laura as having played Bob once, and drawn, and Bob as having played Laura once, and won.&lt;/p&gt;

&lt;p&gt;(In my defense, the Swiss pairing system tries very hard to never pair two players more than once per tournament, so this situation is rare in reality. It is, however, not impossible.)&lt;/p&gt;

&lt;h2&gt;
  
  
  A type-driven solution
&lt;/h2&gt;

&lt;p&gt;The fundamental problem is that we’re using scores for two completely different purposes: as the outcomes of individual matches, and as the overall sum score for ranking.&lt;/p&gt;

&lt;p&gt;Since Coronate is built with Reason now, I have the luxury of enforcing these differences on the type level. &lt;/p&gt;

&lt;p&gt;First, I defined match scores like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Score.rei */&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;Sum&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&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;toFloat&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Zero&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;One&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;NegOne&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Half&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;sum&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Sum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This probably looks self-explanatory. We only have four possible scores: zero, one, one-half, and negative-one. They’re variants, not numbers. &lt;code&gt;Score.sum&lt;/code&gt; takes a list of &lt;code&gt;Score.t&lt;/code&gt; and “adds” them to produce a &lt;code&gt;Score.Sum.t&lt;/code&gt;. &lt;code&gt;Score.Sum.t&lt;/code&gt; is really just a &lt;code&gt;float&lt;/code&gt;, but the implementation is hidden by the interface (more on that later). &lt;code&gt;Score.Sum.toFloat&lt;/code&gt; is a type cast.&lt;/p&gt;

&lt;p&gt;With this, the bug we had encountered previously will be rejected by the compiler. And because our score logic is encapsulated entirely inside one module, there’s little chance of similar bugs happening again. &lt;/p&gt;

&lt;p&gt;I also rewrote &lt;code&gt;opponentResults&lt;/code&gt; as type &lt;code&gt;list((Id.t, Score.t))&lt;/code&gt;. It now actually represents a history, and a reducer function is able to compute the “sum” score against an individual player.&lt;/p&gt;

&lt;p&gt;Our code from earlier looks like this now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Zero&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;NegOne&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;opponentName&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="s2"&gt;" - Lost"&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;One&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;opponentName&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="s2"&gt;" - Won"&lt;/span&gt;
&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Half&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;opponentName&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="s2"&gt;" - Draw"&lt;/span&gt; &lt;span class="cm"&gt;/* no catch-all case needed! */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Continued benefits
&lt;/h2&gt;

&lt;p&gt;This ended up helping me with the other feature I was adding as well. I wanted to add the ability to manually adjust a player’s score. For example, you could add a handicap point for someone. This was tricky because I had to hunt down every place where scores could possibly be used and ensure that the adjustment was being added.&lt;/p&gt;

&lt;p&gt;Once the score code was refactored, this task was easy. Instead of &lt;code&gt;Score.sum&lt;/code&gt;, I made this function instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;calcScore&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;adjustment&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Sum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, any time a score is calculated, an &lt;code&gt;adjustment&lt;/code&gt; &lt;em&gt;must&lt;/em&gt; be applied as well (usually just &lt;code&gt;0.0&lt;/code&gt;). Because &lt;code&gt;Score.Sum.t&lt;/code&gt; is abstract, I’m no longer free to apply whatever math I want to the score anywhere in the code. All of the implementation logic is contained in, and controlled by, the interface. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This is a good example of how you can use Reason-style typing to fully describe how your code is intended to work. When you end up using &lt;code&gt;float&lt;/code&gt; or &lt;code&gt;string&lt;/code&gt; for everything, you will inevitably wind up accidentally misusing the data. As we saw here, a “number” can mean many different things in different contexts.&lt;/p&gt;

</description>
      <category>reason</category>
      <category>bucklescript</category>
    </item>
    <item>
      <title>Building Coronate: an open source chess tournament manager</title>
      <dc:creator>John</dc:creator>
      <pubDate>Sat, 04 Apr 2020 16:44:20 +0000</pubDate>
      <link>https://dev.to/johnridesabike/building-coronate-an-open-source-chess-tournament-manager-46i8</link>
      <guid>https://dev.to/johnridesabike/building-coronate-an-open-source-chess-tournament-manager-46i8</guid>
      <description>&lt;p&gt;&lt;em&gt;May 2021 update: I wrote this article when Coronate was using ReasonML and ReasonReact. Those projects have since been replaced with &lt;a href="https://rescript-lang.org/" rel="noopener noreferrer"&gt;ReScript&lt;/a&gt;. ReScript provides most of the same features, but the naming and syntax is slightly different.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A little over a year ago, I tried to run a chess tournament but found that there was no free software to manage it. In a world where there is free software for essentially everything, this seemed outrageous. I resolved to solve this problem myself. After all, how hard could it be to throw some code together? As these things usually go, it turned out to be much, much more work than I planned. But, eventually, I finally created &lt;a href="https://github.com/johnridesabike/coronate" rel="noopener noreferrer"&gt;Coronate&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The first “official” 1.0 release was last October. Since then, I’ve done a major refactor to bring it in line with best coding practices. Today feels like a good time to review what I learned and what could be done differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1: The plan
&lt;/h2&gt;

&lt;p&gt;When I began, I had a short list of goals for the app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It needed to automatically pair players.&lt;/li&gt;
&lt;li&gt;It needed to calculate tiebreaks (the rules for these are not simple).&lt;/li&gt;
&lt;li&gt;It needed to follow Swiss tournament procedure.&lt;/li&gt;
&lt;li&gt;It needed to run on a (severely limited) public library computer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last goal led me to build it as a web app, so I spun up &lt;a href="https://create-react-app.dev" rel="noopener noreferrer"&gt;&lt;code&gt;create-react-app&lt;/code&gt;&lt;/a&gt; and started learning React. &lt;/p&gt;

&lt;h3&gt;
  
  
  Design decisions and limitations
&lt;/h3&gt;

&lt;p&gt;I still consider Coronate to be largely a “proof of concept.” I didn’t code any kind of backend, or any robust way to handle user data. That is all saved in the  browser’s storage, which is notoriously fragile. I believe the important part of the app right now is laying the groundwork for the future. &lt;/p&gt;

&lt;h3&gt;
  
  
  The transition to Reason
&lt;/h3&gt;

&lt;p&gt;During the early months of development, I had no interest in adopting static typing. In fact, one of my favorite things about React was how easily you could just write code and have the app instantly appear on the screen. I didn’t want complicated type systems slowing me down. &lt;/p&gt;

&lt;p&gt;But as the project matured, I changed my opinion. The number of modules I was maintaining kept growing: player data, match data, score data, pairing data, and pages and pages of components for rendering all of them. In order for anything useful to happen in the app, data from these modules had to be processed across many functions at once. Keeping these bug-free was growing impossible for me alone.&lt;/p&gt;

&lt;p&gt;After playing around with a few JavaScript alternatives, I finally settled on &lt;a href="https://reasonml.github.io" rel="noopener noreferrer"&gt;Reason&lt;/a&gt; and began a rewrite. That brings us to today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: a Reasonable project structure
&lt;/h2&gt;

&lt;p&gt;Note: &lt;a href="https://github.com/johnridesabike/coronate" rel="noopener noreferrer"&gt;Coronate is open source on GitHub&lt;/a&gt;, and feel free to browse it! I do my best to explain the project in this post, but I believe the code speaks for itself much more clearly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Playing nicely with Create React App
&lt;/h3&gt;

&lt;p&gt;CRA works &lt;em&gt;almost&lt;/em&gt; perfectly with Reason out of the box. There are just a few things you need to do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://reasonml.org/docs/reason-react/latest/installation#adding-reason--bucklescript-to-an-existing-project" rel="noopener noreferrer"&gt;Follow the official steps to adding Reason&lt;/a&gt;. (Note: the latest &lt;code&gt;bs-platform&lt;/code&gt; version works fine. Install the latest one!)&lt;/li&gt;
&lt;li&gt;Add this special Jest and ESLint configuration to your &lt;code&gt;package.json&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"jest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"transformIgnorePatterns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"node_modules/(?!(reason-[a-z&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;-]+|@[a-z&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;-]+/bs-[a-z&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;-]+|re-[a-z&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;-]+|bs-[a-z&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;-]+)/)"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eslintConfig"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"extends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"react-app"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"rules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"default-case"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"off"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"no-unused-vars"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"off"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"react-hooks/exhaustive-deps"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"off"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"no-unreachable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"off"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CRA’s default ESLint rules are mostly irrelevant when you’re coding in Reason. The only ones that are definitely worth keeping on are the rules that cover the “rules of hooks.”&lt;/p&gt;

&lt;h3&gt;
  
  
  Project layout
&lt;/h3&gt;

&lt;p&gt;In short, you should go with the &lt;a href="https://reasonml.org/docs/manual/latest/project-structure" rel="noopener noreferrer"&gt;official recommendation&lt;/a&gt;: avoid nested folders and have fewer, larger, files.&lt;/p&gt;

&lt;p&gt;In JavaScript, every file is a module, and it’s natural to organize files in trees of directories. As a result, you’re constantly &lt;code&gt;import&lt;/code&gt;ing and &lt;code&gt;export&lt;/code&gt;ing files across that tree. Reason’s &lt;em&gt;amazing&lt;/em&gt; module system turns that on its head. In addition to the fact that &lt;em&gt;every module is always available with no imports&lt;/em&gt;, you can nest as many modules in each file that you want. It’s seriously great to just reference a function without having to figure out which directory and which file you need, and to be able to see your entire project on one screen when you list your files.&lt;/p&gt;

&lt;p&gt;In Coronate, I only have two sub-directories of Reason code in &lt;code&gt;src&lt;/code&gt;, and both are to group modules that are all used together. (That’s not counting the directories for tests and mocks.) Once you get used to this module system, it feels painful to try navigating the old, JS-style, system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Managing state
&lt;/h3&gt;

&lt;p&gt;I loved &lt;code&gt;useContext&lt;/code&gt; in JavaScript. “Prop drilling,” where you pass props down enormous trees of components, was painful.&lt;/p&gt;

&lt;p&gt;In Reason, context works but isn’t as safe as using old-fashioned props. I discovered that prop-drilling is actually &lt;em&gt;pleasant&lt;/em&gt; with Reason. Because the compiler keeps track of the props so well for you, passing them around doesn’t feel like a chore. But the compiler can’t check context the same way it checks props. By sticking to props, there’s never a worry they’re missing important context.&lt;/p&gt;

&lt;h3&gt;
  
  
  Minimize JavaScript externals
&lt;/h3&gt;

&lt;p&gt;One slowdown with adopting Reason was adding bindings for all of my npm modules.  But I also learned the joy of having as few dependencies as possible. Because Reason has its own standard library built in, a lot of “utility function” packages aren’t necessary. My code is much more readable and maintainable since I only kept the dependencies I absolutely needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 3: Reason’s best practices and patterns
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Interface all the things
&lt;/h3&gt;

&lt;p&gt;I used to &lt;em&gt;hate&lt;/em&gt; &lt;code&gt;rei&lt;/code&gt; files. They were exactly why I didn’t want to use a typed language in the first place. Especially in Reason, where you can write code with &lt;em&gt;zero&lt;/em&gt; type annotations, why go through the trouble of writing the annotations in a separate file, and then worry about keeping it in sync with the implementation file?&lt;/p&gt;

&lt;p&gt;Over time, I realized how useful they can be. Interfaces are a great way to keep yourself organized and look at the “big picture” of your code instead of all of the implementation details. Trying to find the function you need? Just look at the interface file. Looking for better ways to organize your modules? Start by reading the interface files. They also warn you if you broke something. If you refactor an &lt;code&gt;re&lt;/code&gt; file but don’t touch the &lt;code&gt;rei&lt;/code&gt; file, then you can be confident you’ve avoided breaking changes.&lt;/p&gt;

&lt;p&gt;For this latest refactor, I made it goal to add an &lt;code&gt;rei&lt;/code&gt; for &lt;em&gt;every&lt;/em&gt; &lt;code&gt;re&lt;/code&gt; file, even the ones that just export one React component. Yes, it took some work to write them all, but I’m so much more productive and comfortable with my code now that they’re here.&lt;/p&gt;

&lt;h3&gt;
  
  
  One module for one thing
&lt;/h3&gt;

&lt;p&gt;Did I mention how great Reason modules are? I say that the more modules we have, the better. Ideally, we should put every type inside its own module with all of the functions for it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://github.com/johnridesabike/coronate/blob/master/src/Data/Data_Config.rei" rel="noopener noreferrer"&gt;To store configuration data, I had a module like this:&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Data_Config.rei */&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;byeValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Full&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Half&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cm"&gt;/* (With an odd number of players, one is assigned a "bye." That player is
   conventionally awarded either 1 or 0.5 points.) */&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;pair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&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;module&lt;/span&gt; &lt;span class="nc"&gt;PairCmp&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;byeValue&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="cm"&gt;/* avoidPairs stores players who should never be paired together. */&lt;/span&gt;
  &lt;span class="n"&gt;avoidPairs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Belt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;PairCmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;lastBackup&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seems good so far, but what happens when you start adding more functions?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Data_Config.rei */&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;byeValueToFloat&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;byeValue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;float&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;byeValueFromFloat&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;byeValue&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;byeValueEncode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;byeValue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&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;byeValueDecode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;byeValue&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;pairEncode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pair&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&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;pairDecode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/* ...and so on... */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we pile on the functions, it gets more noisy. Modules to the rescue!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Data_Config.rei */&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;ByeValue&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Full&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Half&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;toFloat&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;float&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;fromFloat&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&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;decode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&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;encode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;Pair&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;identity&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;make&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&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;decode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&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;encode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;nonrec&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Belt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;identity&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;empty&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&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="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;avoidPairs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Pair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;byeValue&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;ByeValue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;lastBackup&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;,&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;decode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&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;encode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Much nicer!&lt;/p&gt;

&lt;p&gt;You may object and say reality isn’t always so orderly. What about when you need a function that uses types across multiple modules? If there’s no obvious home for it, then create a new module for it. (This also avoids cyclic dependencies.) As your project matures, the pieces will fall into their right places.&lt;/p&gt;

&lt;h3&gt;
  
  
  Opaque types
&lt;/h3&gt;

&lt;p&gt;Opaque types are another one of my favorite features in Reason. They allow you to define a concrete type in an implementation, like &lt;code&gt;type t = string;&lt;/code&gt;, but then hide it in the interface, so other modules just see &lt;code&gt;type t;&lt;/code&gt;. Even though it’s a string, you can’t use it in string functions outside of its module.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example: identifiers
&lt;/h4&gt;

&lt;p&gt;Every piece of data, person, match, etc, needed a string to identify it. But as far as most of the project is concerned, it doesn’t matter if the id is a string or an int or something else. So I set up an &lt;code&gt;Id&lt;/code&gt; module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Data_Id.re */&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&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;let&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nanoid&lt;/span&gt;&lt;span class="bp"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/* Data_Id.rei */&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&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;random&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;See the complete &lt;a href="https://github.com/johnridesabike/coronate/blob/master/src/Data/Data_Id.re" rel="noopener noreferrer"&gt;&lt;code&gt;Data_Id.re&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/johnridesabike/coronate/blob/master/src/Data/Data_Id.rei" rel="noopener noreferrer"&gt;&lt;code&gt;Data_Id.rei&lt;/code&gt;&lt;/a&gt; files on GitHub.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This has several benefits. You can’t accidentally do a string operation on an &lt;code&gt;id&lt;/code&gt;. It also helps your tooling. If you look at a function signature and it says it needs a &lt;code&gt;string&lt;/code&gt;, what does that mean? A string could be anything! But when it says it needs &lt;code&gt;Data.Id.t&lt;/code&gt;, then that’s much more helpful.&lt;/p&gt;

&lt;p&gt;You can take it to the next level and give every different data type its own id type as well, like &lt;code&gt;Data.Player.Id.t&lt;/code&gt; for example. This makes your code even safer because the &lt;code&gt;id&lt;/code&gt;s for each kind of data can’t be used in the wrong functions. In my case, I didn’t find this necessary, but it could be useful for a larger app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Give everything an explicit type
&lt;/h3&gt;

&lt;p&gt;One of my all time &lt;em&gt;favorite&lt;/em&gt; activities when Reason coding is finding new places to add types. Do you find yourself having to manually constrain certain ranges of numbers? Are you constantly writing &lt;code&gt;if&lt;/code&gt; statements to ensure that people don’t have negative ages, or that people’s names aren’t empty strings? Do you have to deal with several states at once? (E.g.: “loading,” “loaded,” “logged in,” “logged out,”, etc.)&lt;/p&gt;

&lt;p&gt;Just give give them their own types and let the compiler find your bugs for you!&lt;/p&gt;

&lt;h4&gt;
  
  
  Example A: chess match results
&lt;/h4&gt;

&lt;p&gt;I needed a way to store the scores at the end of each match. In chess, the winner receives &lt;code&gt;1&lt;/code&gt; point, the loser receives &lt;code&gt;0&lt;/code&gt; points, and, if it’s a draw, both receive &lt;code&gt;0.5&lt;/code&gt; points. In JavaScript, it made sense to just store the result as one of those numbers, like so: &lt;code&gt;{"whiteScore": 0, "blackScore": 1}&lt;/code&gt;. Then you can easily add them up and calculate the total scores. If you’re given a match and need to know who won, just look at who got the &lt;code&gt;1&lt;/code&gt;, or if both players got &lt;code&gt;0.5&lt;/code&gt;, or if both players have &lt;code&gt;0&lt;/code&gt; to know if it was a draw or not scored yet.&lt;/p&gt;

&lt;p&gt;As you can imagine, this adds up to a lot of possible states. In JavaScript, I had to nest &lt;code&gt;if&lt;/code&gt; statements and pray I wasn’t forgetting something. As soon as you try to render a different UI for every possible outcome of a match, it’s a nightmare to keep straight.&lt;/p&gt;

&lt;p&gt;Reason simplifies all of that logic to one type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;WhiteWon&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;BlackWon&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Draw&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;NotSet&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Could this code ever confuse anyone? And thanks to Reason’s pattern matching and exhaustive checking, it’s almost impossible for you to render the wrong UI, and it quite literally &lt;em&gt;is impossible&lt;/em&gt; to try to access an illegal state.&lt;/p&gt;

&lt;p&gt;Of course, the numerical values are still necessary for calculating scores, but adding helper functions for that is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Interface: */&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;toFloatWhite&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;float&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;toFloatBlack&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/* Example use: */&lt;/span&gt;
&lt;span class="n"&gt;toFloatWhite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;WhiteWon&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="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;toFloatWhite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BlackWon&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;toFloatWhite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Draw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&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;toFloatWhite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;NotSet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Example B: pairs
&lt;/h4&gt;

&lt;p&gt;Remember the &lt;code&gt;Pair&lt;/code&gt; module from earlier? When I refactored it, I added some extra logic to make it even more robust. Originally, i had &lt;code&gt;type t = (string, string)&lt;/code&gt;, but this has problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It’s possible to pair a player with themselves.&lt;/li&gt;
&lt;li&gt;Comparing pairs is complicated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After my refactor, it became this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Implementation: */&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;Pair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&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;make&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;None&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;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&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;With this signature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Interface: */&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;Pair&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&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;make&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&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;Note: &lt;code&gt;Pair.t&lt;/code&gt; is &lt;em&gt;opaque&lt;/em&gt; now. Outside of its module, there’s no way we can directly access or manipulate the data inside the type. &lt;code&gt;Pair.make&lt;/code&gt; checks to see if the two ids are equal, and it returns &lt;code&gt;None&lt;/code&gt; if they are. If they aren’t, it sorts them before returning the result. When they’re guaranteed to be sorted, it’s much easier to safely compare them.&lt;/p&gt;

&lt;p&gt;Not only does this eliminate a whole range of bugs happening across your code, it also makes your code a lot easier to write and understand. When you’re writing the UI, you don’t need to worry about what a &lt;code&gt;Pair.t&lt;/code&gt; is or how it’s structured. The &lt;code&gt;Pair&lt;/code&gt; module takes care of all that for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Type-safe router
&lt;/h3&gt;

&lt;p&gt;URLs, are, unfortunately, not smart. They’re just strings that are supposed to represent some kind of location, but they can’t do much besides that. Can you imagine if your 404 errors were all caught at compile time? With Reason, it’s easy!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.minima.app/posts/2020/reasonml-safe-routing" rel="noopener noreferrer"&gt;Inspired by this guide&lt;/a&gt;, I implemented a typed router. I won’t reiterate what the guide says, but here’s what my interface for the router looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;TourneyPage&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Players&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Scores&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Crosstable&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Setup&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Status&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Round&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="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Index&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;TournamentList&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Tournament&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;TourneyPage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;PlayerList&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Player&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;TimeCalculator&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Options&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;NotFound&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;useHashUrl&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nc"&gt;HashLink&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;react&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;component&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;make&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;to_&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;element&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;A value like &lt;code&gt;Tournament(id, Round(2))&lt;/code&gt; represents the URL for the third round in the tournament with that &lt;code&gt;id&lt;/code&gt;. Notice how my &lt;code&gt;HashLink&lt;/code&gt; component doesn’t accept a string as its &lt;code&gt;to_&lt;/code&gt; prop, it only accepts one of the defined variants. 404 errors are now almost impossible. If there are any bugs in the URLs, they can only exist inside that router module.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing
&lt;/h3&gt;

&lt;p&gt;Once your code is fully immersed in Reason’s type system, testing takes a different character. In many cases, unit testing is no longer necessary. Remember the match result example? In JavaScript, you may need several tests to check that you aren’t creating illegal states or using the results incorrectly. With Reason, there’s no point in testing that, because misusing it is impossible.&lt;/p&gt;

&lt;p&gt;Unit tests still have their place, though. For example, Coronate uses a basic Elo-rating algorithm to match players, and I test those functions to guarantee that the math is correct. In another component, I render dates in different formats, and so I test to make sure the formatting is what I expect.&lt;/p&gt;

&lt;p&gt;But for the most part, unit testing is less of a focus in Reason. This frees up more time to focus on testing how users actually use the app, not the minutia of implementation details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 4: Reason package shoutouts
&lt;/h2&gt;

&lt;p&gt;The Reason ecosystem is still small, but has some high-quality packages. Here are a few that I use in Coronate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/MinimaHQ/re-formality" rel="noopener noreferrer"&gt;Formality&lt;/a&gt;: Not just a good Reason package, but a good solution for web forms in general. Extremely well thought out and easy to use. &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/glennsl/bs-json" rel="noopener noreferrer"&gt;bs-json&lt;/a&gt;: I use this to encode everything, all of my records, variants, and anything else that needs to be stored.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/reasonml-labs/bs-css" rel="noopener noreferrer"&gt;bs-css&lt;/a&gt;: As far as CSS-in-JS solutions go (or CSS-in-Re solutions), this one is great in how it covers almost all CSS in a type-safe manner.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/MinimaHQ/re-classnames" rel="noopener noreferrer"&gt;re-classnames&lt;/a&gt;: It’s simple and it gets the job done. &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/aantron/promise" rel="noopener noreferrer"&gt;Reason Promise&lt;/a&gt;: Promises are still a bit clunky in Reason, but this handles them well.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/glennsl/bs-jest" rel="noopener noreferrer"&gt;bs-jest&lt;/a&gt;: Reason bindings for Jest. &lt;em&gt;It may install a version of Jest that isn’t compatible with Create React App, so be sure to make sure you have the correct Jest installed too.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building Coronate was a long journey. In an alternate universe, it may have been built in TypeScript or Elm, but I’m extremely pleased with Reason. The more I code Reason, the more I like it. It just feels so great to see all of your modules logically click into place. Given Reason's relatively small ecosystem, this says a lot about the quality of the language itself.&lt;/p&gt;

&lt;p&gt;I’m also keeping my eye on the native side of Reason. &lt;a href="https://www.outrunlabs.com/revery/" rel="noopener noreferrer"&gt;Revery&lt;/a&gt; looks very promising. (I may use it for a Coronate 2.0 or another future project.) If you’re interested in getting into Reason, now is a great time to learn! The community is working to &lt;a href="https://reasonml.org" rel="noopener noreferrer"&gt;improve the documentation&lt;/a&gt;, and the &lt;a href="https://discord.gg/rYgjzc" rel="noopener noreferrer"&gt;Discord chat&lt;/a&gt; is welcoming. &lt;/p&gt;

&lt;p&gt;If you found this helpful and have any questions about Reason or Coronate, just let me know!&lt;/p&gt;

</description>
      <category>reason</category>
      <category>webdev</category>
      <category>chess</category>
    </item>
  </channel>
</rss>
