<?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: Simon Shine</title>
    <description>The latest articles on DEV Community by Simon Shine (@sshine).</description>
    <link>https://dev.to/sshine</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%2F214768%2Ff52eccb1-e0b3-41a2-843b-e1c8747bfcad.png</url>
      <title>DEV Community: Simon Shine</title>
      <link>https://dev.to/sshine</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sshine"/>
    <language>en</language>
    <item>
      <title>JSON Schema in Haskell using AutoDoCodec</title>
      <dc:creator>Simon Shine</dc:creator>
      <pubDate>Sat, 26 Jul 2025 21:02:21 +0000</pubDate>
      <link>https://dev.to/sshine/json-schema-in-haskell-using-autodocodec-35cg</link>
      <guid>https://dev.to/sshine/json-schema-in-haskell-using-autodocodec-35cg</guid>
      <description>&lt;p&gt;In April 2022 I made a &lt;a href="https://dev.to/sshine/a-review-of-json-schema-libraries-for-haskell-321"&gt;review of Haskell libraries for JSON Schema&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The outcome of my research was that there weren't really any good libraries for dealing with &lt;a href="https://json-schema.org/" rel="noopener noreferrer"&gt;JSON Schema&lt;/a&gt; from within Haskell. That was a surprising disappointment, because Haskell often has very good libraries (although they may be hard to use). The reason is that the JSON Schema specification itself isn't very well-made.&lt;/p&gt;

&lt;p&gt;I eventually gave up using any of them.&lt;/p&gt;

&lt;p&gt;Today &lt;a href="https://tarleb.com/about.html" rel="noopener noreferrer"&gt;Albert Krewinkel&lt;/a&gt; wrote me and told me that you can actually use &lt;a href="https://github.com/NorfairKing/autodocodec" rel="noopener noreferrer"&gt;AutoDoCodec&lt;/a&gt; to both generate and validate JSON schemas. The Haskell package &lt;a href="https://hackage.haskell.org/package/autodocodec" rel="noopener noreferrer"&gt;autodocodec&lt;/a&gt; was announced in &lt;a href="https://haskellweekly.news/issue/291.html" rel="noopener noreferrer"&gt;Haskell Weekly #291&lt;/a&gt; in November 2021 by &lt;a href="https://github.com/NorfairKing/" rel="noopener noreferrer"&gt;Tom Syd Kerckhove&lt;/a&gt;. I knew this library, but had never made the connection that it'd serve all my JSON schema needs. Since my original blog post felt like a dud, here is another post with a happy ending.&lt;/p&gt;

&lt;p&gt;AutoDoCodec provides a unified approach to defining codecs that can both serialize/deserialize JSON and generate corresponding JSON schemas automatically. It eliminates the common problem of having separate validation logic and schema definitions that can drift out of sync over time.&lt;/p&gt;

&lt;p&gt;Since the JSON schema application as an example is a little dispersed, here's a full example of how one can define a data type, generate its schema, and validate a JSON object against it. You can read the &lt;a href="https://github.com/sshine/json-schema-autodocodec" rel="noopener noreferrer"&gt;full source code for the example&lt;/a&gt; on GitHub.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Define your data type and codec
&lt;/h2&gt;

&lt;p&gt;First, let's define a simple data type with a string, integer, and boolean field, along with its AutoDoCodec instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="cp"&gt;{-# LANGUAGE DeriveGeneric #-}&lt;/span&gt;
&lt;span class="cp"&gt;{-# LANGUAGE DerivingStrategies #-}&lt;/span&gt;
&lt;span class="cp"&gt;{-# LANGUAGE OverloadedStrings #-}&lt;/span&gt;

&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Autodocodec&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Data.Aeson&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ToJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kt"&gt;FromJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;GHC.Generics&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Generic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kr"&gt;data&lt;/span&gt; &lt;span class="kt"&gt;Person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Person&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;personName&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="n"&gt;personAge&lt;/span&gt; &lt;span class="o"&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;personIsActive&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="kr"&gt;deriving&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Show&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Eq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Generic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kr"&gt;instance&lt;/span&gt; &lt;span class="kt"&gt;HasCodec&lt;/span&gt; &lt;span class="kt"&gt;Person&lt;/span&gt; &lt;span class="kr"&gt;where&lt;/span&gt;
  &lt;span class="n"&gt;codec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="s"&gt;"Person"&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;
    &lt;span class="kt"&gt;Person&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;$&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;requiredField&lt;/span&gt; &lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="s"&gt;"The person's name"&lt;/span&gt; &lt;span class="o"&gt;.=&lt;/span&gt; &lt;span class="n"&gt;personName&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;*&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;requiredField&lt;/span&gt; &lt;span class="s"&gt;"age"&lt;/span&gt; &lt;span class="s"&gt;"The person's age in years"&lt;/span&gt; &lt;span class="o"&gt;.=&lt;/span&gt; &lt;span class="n"&gt;personAge&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;*&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;requiredField&lt;/span&gt; &lt;span class="s"&gt;"isActive"&lt;/span&gt; &lt;span class="s"&gt;"Whether the person is currently active"&lt;/span&gt; &lt;span class="o"&gt;.=&lt;/span&gt; &lt;span class="n"&gt;personIsActive&lt;/span&gt;

&lt;span class="kr"&gt;instance&lt;/span&gt; &lt;span class="kt"&gt;ToJSON&lt;/span&gt; &lt;span class="kt"&gt;Person&lt;/span&gt; &lt;span class="kr"&gt;where&lt;/span&gt;
  &lt;span class="n"&gt;toJSON&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;toJSONViaCodec&lt;/span&gt;

&lt;span class="kr"&gt;instance&lt;/span&gt; &lt;span class="kt"&gt;FromJSON&lt;/span&gt; &lt;span class="kt"&gt;Person&lt;/span&gt; &lt;span class="kr"&gt;where&lt;/span&gt;
  &lt;span class="n"&gt;parseJSON&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parseJSONViaCodec&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;HasCodec&lt;/code&gt; instance defines both the JSON structure and provides field descriptions that will appear in the generated schema. The &lt;code&gt;ToJSON&lt;/code&gt; and &lt;code&gt;FromJSON&lt;/code&gt; instances are automatically derived from the codec definition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Generate the JSON schema
&lt;/h2&gt;

&lt;p&gt;Now we can generate a complete JSON schema from our data type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Autodocodec.Schema&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;jsonSchemaViaCodec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Data.Aeson.Encode.Pretty&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;encodePretty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Data.ByteString.Lazy.Char8&lt;/span&gt; &lt;span class="n"&gt;qualified&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;L8&lt;/span&gt;

&lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;IO&lt;/span&gt; &lt;span class="nb"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;do&lt;/span&gt;
  &lt;span class="kr"&gt;let&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jsonSchemaViaCodec&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="kt"&gt;Person&lt;/span&gt;
  &lt;span class="n"&gt;putStrLn&lt;/span&gt; &lt;span class="s"&gt;"Generated JSON Schema:"&lt;/span&gt;
  &lt;span class="kt"&gt;L8&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;putStrLn&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="n"&gt;encodePretty&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This produces a complete JSON schema with type information, descriptions, and validation rules:&lt;br&gt;
&lt;/p&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;"$comment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Person"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"properties"&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;"age"&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;"$comment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The person's age in years"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"maximum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9223372036854775807&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"minimum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-9223372036854775808&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"integer"&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;"isActive"&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;"$comment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Whether the person is currently active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"boolean"&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;"name"&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;"$comment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The person's name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&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;"required"&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="s2"&gt;"isActive"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&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;h2&gt;
  
  
  Step 3: Validate JSON against the schema
&lt;/h2&gt;

&lt;p&gt;AutoDoCodec provides built-in validation functions to check JSON against the generated schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Autodocodec.Schema&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;validateAccordingTo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Data.Aeson&lt;/span&gt; &lt;span class="n"&gt;qualified&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Aeson&lt;/span&gt;

&lt;span class="c1"&gt;-- Valid JSON example&lt;/span&gt;
&lt;span class="kr"&gt;let&lt;/span&gt; &lt;span class="n"&gt;validJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Aeson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="kt"&gt;Aeson&lt;/span&gt;&lt;span class="o"&gt;..=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Alice"&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="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"age"&lt;/span&gt; &lt;span class="kt"&gt;Aeson&lt;/span&gt;&lt;span class="o"&gt;..=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="o"&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="s"&gt;"isActive"&lt;/span&gt; &lt;span class="kt"&gt;Aeson&lt;/span&gt;&lt;span class="o"&gt;..=&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;-- Invalid JSON example (age as string instead of number)&lt;/span&gt;
&lt;span class="kr"&gt;let&lt;/span&gt; &lt;span class="n"&gt;invalidJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Aeson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="kt"&gt;Aeson&lt;/span&gt;&lt;span class="o"&gt;..=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bob"&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="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"age"&lt;/span&gt; &lt;span class="kt"&gt;Aeson&lt;/span&gt;&lt;span class="o"&gt;..=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"not-a-number"&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="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;-- Validate using the schema&lt;/span&gt;
&lt;span class="kr"&gt;if&lt;/span&gt; &lt;span class="n"&gt;validateAccordingTo&lt;/span&gt; &lt;span class="n"&gt;validJson&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt;
  &lt;span class="kr"&gt;then&lt;/span&gt; &lt;span class="n"&gt;putStrLn&lt;/span&gt; &lt;span class="s"&gt;"Valid JSON accepted"&lt;/span&gt;
  &lt;span class="kr"&gt;else&lt;/span&gt; &lt;span class="n"&gt;putStrLn&lt;/span&gt; &lt;span class="s"&gt;"Valid JSON rejected"&lt;/span&gt;

&lt;span class="kr"&gt;if&lt;/span&gt; &lt;span class="n"&gt;validateAccordingTo&lt;/span&gt; &lt;span class="n"&gt;invalidJson&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt;
  &lt;span class="kr"&gt;then&lt;/span&gt; &lt;span class="n"&gt;putStrLn&lt;/span&gt; &lt;span class="s"&gt;"Invalid JSON incorrectly accepted"&lt;/span&gt;
  &lt;span class="kr"&gt;else&lt;/span&gt; &lt;span class="n"&gt;putStrLn&lt;/span&gt; &lt;span class="s"&gt;"Invalid JSON correctly rejected"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more detailed error messages, you can use the parsing functions directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;case&lt;/span&gt; &lt;span class="kt"&gt;Aeson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromJSON&lt;/span&gt; &lt;span class="n"&gt;invalidJson&lt;/span&gt; &lt;span class="kr"&gt;of&lt;/span&gt;
  &lt;span class="kt"&gt;Aeson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Success&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;_&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Person&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;putStrLn&lt;/span&gt; &lt;span class="s"&gt;"Parsed successfully"&lt;/span&gt;
  &lt;span class="kt"&gt;Aeson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Error&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;putStrLn&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt; &lt;span class="s"&gt;"Parsing failed: "&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="c1"&gt;-- Output: "Parsing failed: parsing Scientific failed, expected Number, but encountered String"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Adding validation constraints
&lt;/h2&gt;

&lt;p&gt;AutoDoCodec provides built-in support for adding validation constraints to both numeric and string fields.&lt;/p&gt;

&lt;h3&gt;
  
  
  Numeric constraints
&lt;/h3&gt;

&lt;p&gt;To constrain the age field to reasonable values between 0 and 200:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Define age bounds: 0 to 200 years&lt;/span&gt;
&lt;span class="n"&gt;ageBounds&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Bounds&lt;/span&gt; &lt;span class="kt"&gt;Integer&lt;/span&gt;
&lt;span class="n"&gt;ageBounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Bounds&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  String length constraints
&lt;/h3&gt;

&lt;p&gt;To ensure the name field is not empty:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Name length bounds: at least 1 character (non-empty)&lt;/span&gt;
&lt;span class="n"&gt;nameBounds&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Bounds&lt;/span&gt; &lt;span class="kt"&gt;Word&lt;/span&gt;
&lt;span class="n"&gt;nameBounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Bounds&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Just&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;Nothing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Updated codec with constraints
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="kr"&gt;instance&lt;/span&gt; &lt;span class="kt"&gt;HasCodec&lt;/span&gt; &lt;span class="kt"&gt;Person&lt;/span&gt; &lt;span class="kr"&gt;where&lt;/span&gt;
  &lt;span class="n"&gt;codec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="s"&gt;"Person"&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;
    &lt;span class="kt"&gt;Person&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;$&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;requiredFieldWith&lt;/span&gt; &lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;textWithLengthBoundsCodec&lt;/span&gt; &lt;span class="n"&gt;nameBounds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="s"&gt;"The person's name (non-empty)"&lt;/span&gt; &lt;span class="o"&gt;.=&lt;/span&gt; &lt;span class="n"&gt;personName&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;*&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;requiredFieldWith&lt;/span&gt; &lt;span class="s"&gt;"age"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dimapCodec&lt;/span&gt; &lt;span class="n"&gt;fromIntegral&lt;/span&gt; &lt;span class="n"&gt;fromIntegral&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;integerWithBoundsCodec&lt;/span&gt; &lt;span class="n"&gt;ageBounds&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="s"&gt;"The person's age (0-200 years)"&lt;/span&gt; &lt;span class="o"&gt;.=&lt;/span&gt; &lt;span class="n"&gt;personAge&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;*&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;requiredField&lt;/span&gt; &lt;span class="s"&gt;"isActive"&lt;/span&gt; &lt;span class="s"&gt;"Whether the person is currently active"&lt;/span&gt; &lt;span class="o"&gt;.=&lt;/span&gt; &lt;span class="n"&gt;personIsActive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This automatically generates JSON schema with proper validation constraints:&lt;br&gt;
&lt;/p&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;"name"&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;"$comment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The person's name (non-empty)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"minLength"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&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;"age"&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;"$comment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The person's age (0-200 years)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"maximum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"minimum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"integer"&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;The constraints are enforced both during JSON parsing and schema validation, ensuring invalid values like empty names or ages outside 0-200 are automatically rejected.&lt;/p&gt;

&lt;p&gt;The beauty of AutoDoCodec is that your codec definition serves as both the source of truth for JSON serialization/deserialization &lt;em&gt;and&lt;/em&gt; schema generation. This eliminates the common problem where validation logic and schema definitions drift apart over time, ensuring they always stay in sync.&lt;/p&gt;

</description>
      <category>haskell</category>
      <category>json</category>
      <category>jsonschema</category>
    </item>
    <item>
      <title>Minimal Sufficient Groups</title>
      <dc:creator>Simon Shine</dc:creator>
      <pubDate>Sun, 02 Feb 2025 16:36:48 +0000</pubDate>
      <link>https://dev.to/sshine/minimal-sufficient-groups-1dj3</link>
      <guid>https://dev.to/sshine/minimal-sufficient-groups-1dj3</guid>
      <description>&lt;p&gt;This is a programming interview question I got some years ago.&lt;/p&gt;

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

&lt;p&gt;When a certain company creates a new project, they enumerate the skills required for the project, and they form a project group where each of those skills are represented. Project groups have two rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sufficient:&lt;/strong&gt; Each required skill should be represented with at least one group member.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Minimal:&lt;/strong&gt; Each group member should be essential, so removing any one member from the team should make the rest of the team no longer sufficient. Members can have overlapping skills, as long as everyone is essential in some capacity.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The task is, given a set of employees and their skill sets, and a set of required skills, provide all possible minimal, sufficient project groups.&lt;/p&gt;

&lt;h2&gt;
  
  
  My solution
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="cp"&gt;{-# LANGUAGE RankNTypes #-}&lt;/span&gt;
&lt;span class="cp"&gt;{-# LANGUAGE ScopedTypeVariables #-}&lt;/span&gt;

&lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;MinimalProjectGroups&lt;/span&gt; &lt;span class="kr"&gt;where&lt;/span&gt;

&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Data.Map.Strict&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="k"&gt;qualified&lt;/span&gt; &lt;span class="nn"&gt;Data.Map.Strict&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;Map&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Data.Set&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="k"&gt;qualified&lt;/span&gt; &lt;span class="nn"&gt;Data.Set&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;Set&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;Data.Text&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="k"&gt;qualified&lt;/span&gt; &lt;span class="nn"&gt;Data.Text&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;

&lt;span class="n"&gt;minimalSufficientGroups&lt;/span&gt;
  &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;forall&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="n"&gt;forall&lt;/span&gt; &lt;span class="n"&gt;skill&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Ord&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Ord&lt;/span&gt; &lt;span class="n"&gt;skill&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Set&lt;/span&gt; &lt;span class="n"&gt;skill&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt; &lt;span class="n"&gt;skill&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Set&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;minimalSufficientGroups&lt;/span&gt; &lt;span class="n"&gt;staff&lt;/span&gt; &lt;span class="n"&gt;requirements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;\&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;isSufficient&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;isMinimal&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;allGroups&lt;/span&gt;
  &lt;span class="kr"&gt;where&lt;/span&gt;
    &lt;span class="n"&gt;allGroups&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Ord&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Set&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;allGroups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;powerSet&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keysSet&lt;/span&gt; &lt;span class="n"&gt;staff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;isSufficient&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Ord&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
    &lt;span class="n"&gt;isSufficient&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="n"&gt;requirements&lt;/span&gt; &lt;span class="p"&gt;`&lt;/span&gt;&lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isSubsetOf&lt;/span&gt;&lt;span class="p"&gt;`&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unions&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;skillsOf&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;skillsOf&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Ord&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Ord&lt;/span&gt; &lt;span class="n"&gt;skill&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;employee&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt; &lt;span class="n"&gt;skill&lt;/span&gt;
    &lt;span class="n"&gt;skillsOf&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;findWithDefault&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="n"&gt;staff&lt;/span&gt;

    &lt;span class="n"&gt;isMinimal&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Ord&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
    &lt;span class="n"&gt;isMinimal&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="p"&gt;(`&lt;/span&gt;&lt;span class="n"&gt;isEssentialFor&lt;/span&gt;&lt;span class="p"&gt;`&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;

    &lt;span class="n"&gt;isEssentialFor&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Ord&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
    &lt;span class="n"&gt;isEssentialFor&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isSufficient&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt; &lt;span class="n"&gt;employee&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I optimized for natural readability and type generality.&lt;/p&gt;

&lt;p&gt;Solving it today I would have factored in the total memory usage of the program, since power sets are exponential. While laziness helps a little, I have come to appreciate explicit control of program memory.&lt;/p&gt;

</description>
      <category>haskell</category>
    </item>
    <item>
      <title>Why you should be careful with the Default trait/typeclass</title>
      <dc:creator>Simon Shine</dc:creator>
      <pubDate>Tue, 25 Oct 2022 07:27:54 +0000</pubDate>
      <link>https://dev.to/sshine/why-you-should-be-careful-with-the-default-traittypeclass-3i5m</link>
      <guid>https://dev.to/sshine/why-you-should-be-careful-with-the-default-traittypeclass-3i5m</guid>
      <description>&lt;p&gt;You heard it. &lt;strong&gt;&lt;code&gt;Default&lt;/code&gt; considered harmful!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;tl;dr:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Default&lt;/code&gt; has no defining or testable properties&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Default&lt;/code&gt; is not even guaranteed to be the same between runs&lt;/li&gt;
&lt;li&gt;Defaults are context-specific: One type does not always have one default&lt;/li&gt;
&lt;li&gt;Defaults are useful for transitive derivation, but context drift risks causing errors&lt;/li&gt;
&lt;li&gt;Your &lt;code&gt;Default&lt;/code&gt; instance is dangerous if you cannot carelessly replace it with any other value&lt;/li&gt;
&lt;li&gt;Haskellers: Use the &lt;code&gt;default*&lt;/code&gt; pattern instead&lt;/li&gt;
&lt;li&gt;Rustaceans: Use &lt;code&gt;Default&lt;/code&gt; with care and respect&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;Default&lt;/code&gt; in Rust and in Haskell
&lt;/h2&gt;

&lt;p&gt;Rust's standard library has a &lt;a href="https://doc.rust-lang.org/std/default/trait.Default.html"&gt;&lt;code&gt;Default&lt;/code&gt; trait&lt;/a&gt; that lets you provide a default value for any type:&lt;/p&gt;

&lt;blockquote&gt;

&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="nb"&gt;Default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&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;p&gt;Returns the "default value" for a type.&lt;/p&gt;

&lt;p&gt;Default values are often some kind of initial value, identity value, or anything else that may make sense as a default. Sometimes, you want to fall back to some kind of default value, and &lt;em&gt;don't particularly care what it is.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;code&gt;Default&lt;/code&gt; type class never made it to Haskell's standard library; one lives in the &lt;a href="https://hackage.haskell.org/package/data-default"&gt;&lt;code&gt;data-default&lt;/code&gt;&lt;/a&gt; package, and another one with much more pleasing defaults (sic) lives in &lt;a href="https://hackage.haskell.org/package/acme-default/docs/Data-Default.html"&gt;&lt;code&gt;acme-default&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When asking Haskellers why, the typical reason is that "type classes should reflect mathematical properties (invariants), and &lt;code&gt;Default&lt;/code&gt; does not have any properties": Testing an instance of &lt;code&gt;Default&lt;/code&gt; without making additional assumptions about the type gives you no good things to assert.&lt;/p&gt;

&lt;p&gt;The Rust ecosystem does not have a similar mathematical vigilance around the use of traits: You are much more free to define traits in Rust than type classes in Haskell, and they don't have to reflect mathematical depth. Both of these attitudes are fine, so to dig into why &lt;code&gt;Default&lt;/code&gt; is also bad in Rust, we must dig a little deeper.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defaults are useful, but &lt;code&gt;Default&lt;/code&gt; is sketchy
&lt;/h2&gt;

&lt;p&gt;Software configuration relies heavily on default values.&lt;/p&gt;

&lt;p&gt;Defaults let you run complicated software without having read the complete manual.&lt;/p&gt;

&lt;p&gt;The features that require custom configuration (such as external APIs) can be disabled by default.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; The default database can be SQLite for evaluating the software or for a lighter setup.&lt;/li&gt;
&lt;li&gt; The default logging can be on-screen or in an operating system default directory.&lt;/li&gt;
&lt;li&gt; The default host/port can be 127.0.0.1:3000, so you don't accidentally expose the service when toying around on semi-public networks.&lt;/li&gt;
&lt;li&gt; Using your current working directory as the default directory sometimes works.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The implied context here is "a user will run the software and react to any unintended behavior caused by defaults". When the notion of configuration defaults is taken to a programmatic extreme, the implied context is not necessarily true.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the default integer?
&lt;/h2&gt;

&lt;p&gt;3000 seems like a good choice if it's a generic service port number. 0 or 1 both seem like good choices if it's a monoid. If it's a service port number, 0 will mean "&lt;a href="https://eklitzke.org/binding-on-port-zero"&gt;kernel picks a randomly unused port number&lt;/a&gt;", which does not produce deterministic behavior. If it's an IPv4 address, &lt;a href="https://www.lifewire.com/four-zero-ip-address-818384"&gt;listening on 0.0.0.0&lt;/a&gt; means you will listen on all interfaces, including public ones. Should the default HTTP client parameters be with or without SSL? For simplicity: without. For security: with. It depends on context. You &lt;em&gt;may think&lt;/em&gt; that your type has a well-defined default, but nesting it inside a bigger structure may cause that to no longer be the case, because &lt;em&gt;defaultness does not transcend&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;An example of this drift from the real world is when XMonad's &lt;a href="https://github.com/xmonad/xmonad/commit/383ffb772e4a"&gt;&lt;code&gt;grabKey&lt;/code&gt; grabbed all unbound keys&lt;/a&gt;, because "&lt;code&gt;KeySym 0 (NoSymbol)&lt;/code&gt; gets mapped to every unbound &lt;code&gt;KeyCode&lt;/code&gt;, since that's what &lt;code&gt;XKeycodeToKeysym&lt;/code&gt; returns for those." So while &lt;code&gt;KeySym 0 (NoSymbol)&lt;/code&gt; is an obvious pick, it isn't so good inside &lt;code&gt;XKeycodeToKeysym&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Another example is when a default prime-field element, 0, caused &lt;code&gt;.mul_inverse()&lt;/code&gt; to fail. What a poor implementation of multiplicative inverses when it doesn't even work for the &lt;em&gt;default&lt;/em&gt; prime-field element! The &lt;em&gt;default&lt;/em&gt;, I tell you! (...and I haven't told you much, because defaults are not characterised by anything &lt;em&gt;in general&lt;/em&gt;.)&lt;/p&gt;

&lt;p&gt;The bad examples of defaults are general to any programming language.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does your &lt;code&gt;Default&lt;/code&gt; &lt;em&gt;do&lt;/em&gt; anything?
&lt;/h2&gt;

&lt;p&gt;To check if you understand and appreciate how arbitrary defaults should be capable of being, try and see if you agree with some of the instances found in &lt;a href="https://hackage.haskell.org/package/acme-default/docs/Data-Default.html"&gt;&lt;code&gt;acme-default&lt;/code&gt;&lt;/a&gt; package:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;Default False&lt;/code&gt; is the answer to the question whether mniip has a favourite Bool.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Default Char&lt;/code&gt; is &lt;code&gt;'→'&lt;/code&gt; because arrows look fancy when you use them in a chat.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Default Double&lt;/code&gt; is &lt;code&gt;1.1102230246251565e-16&lt;/code&gt; as the difference between 1 and &lt;code&gt;sum (replicate 10 0.1)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Default Float&lt;/code&gt; is &lt;code&gt;388.38&lt;/code&gt;, which is approximately equal to twice the molar mass of caffeine in grams per mol.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Default Int&lt;/code&gt; is &lt;code&gt;18871&lt;/code&gt;, which is the product of a Sophie-Germain prime and a safe prime. You know, for safety &lt;em&gt;and&lt;/em&gt; for Sophie.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Default Int8&lt;/code&gt; is &lt;code&gt;29&lt;/code&gt; for &lt;em&gt;obvious reasons&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The mockery here serves to highlight the conceptual mistake that defaults are often embedded with meaning that is not warranted in light of being a default, because defaults don't have special meaning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rust: A &lt;code&gt;Default&lt;/code&gt; case-study
&lt;/h2&gt;

&lt;p&gt;An example of when &lt;code&gt;Default&lt;/code&gt; is used in Rust can be found in the &lt;a href="https://github.com/tokio-rs/axum/blob/main/axum/src/routing/mod.rs#L88-L96"&gt;Axum web framework&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;S&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;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Default&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Router&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;S&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;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt;
    &lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HttpBody&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;'static&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Default&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Clone&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;'static&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with_state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;S&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&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;It says that a web app's state must &lt;code&gt;impl Default&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This makes you able to start a web app "from scratch".&lt;/p&gt;

&lt;p&gt;Is this made carefully?&lt;/p&gt;

&lt;p&gt;Because Axum is a web framework and not a web app, providing a generic way to pass the default starting state also passes on the responsibility of defining sensible defaults to the web app developer. Different web apps may use "default" to mean "initial", "empty", or "demo" at their own discretion.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Default&lt;/code&gt;'s lack of any properties seems to make it a legit placeholder here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Implicit assumptions about the properties of a default for some specific type may hold in some contexts. They may also eventually propagate into errors, like in the examples above. Any legal value of a type should be a valid default.&lt;/p&gt;

&lt;p&gt;You may prefer one default over another, but you shouldn't have to.&lt;/p&gt;

&lt;p&gt;The recommended pattern in Haskell is to have named value bindings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight haskell"&gt;&lt;code&gt;&lt;span class="cd"&gt;-- | The default configuration for trying out the software&lt;/span&gt;
&lt;span class="n"&gt;defaultConfig&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;Config&lt;/span&gt;
&lt;span class="n"&gt;defaultConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="cd"&gt;-- | The default shortcut key for interacting with the software&lt;/span&gt;
&lt;span class="n"&gt;defaultModMask&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="kt"&gt;ModMask&lt;/span&gt;
&lt;span class="n"&gt;defaultModMask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;controlMask&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;so that any special context can be referred to in the name of the binding.&lt;/p&gt;

&lt;p&gt;In Rust we're stuck with having &lt;code&gt;Default&lt;/code&gt; in the standard library, so we cannot treat it with the same degree of aversion, since it may occur in the wild. Instead we have to treat it with respect and care, because its only pseudo-property is fragile: It shouldn't matter what the default is.&lt;/p&gt;

&lt;p&gt;While avoiding &lt;code&gt;Default&lt;/code&gt; in Rust is not a general recommendation, the practice of naming things transcends languages. If an &lt;code&gt;impl Default&lt;/code&gt; demonstrates other properties (i.e. needs to be some value to work, or breaks something when changed), it is probably more than a default. It doesn't have to be the &lt;code&gt;impl&lt;/code&gt; of any trait, and naming it may be a better option.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do you have examples of when a sketchy &lt;code&gt;Default&lt;/code&gt; caused problems down the line?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Cover art generated by &lt;a href="https://dreamstudio.ai"&gt;DreamStudio.ai&lt;/a&gt; asking it to draw "default")&lt;/em&gt;&lt;/p&gt;

</description>
      <category>haskell</category>
      <category>rust</category>
      <category>traits</category>
      <category>typeclasses</category>
    </item>
    <item>
      <title>A review of JSON Schema libraries for Haskell</title>
      <dc:creator>Simon Shine</dc:creator>
      <pubDate>Sun, 10 Apr 2022 14:14:14 +0000</pubDate>
      <link>https://dev.to/sshine/a-review-of-json-schema-libraries-for-haskell-321</link>
      <guid>https://dev.to/sshine/a-review-of-json-schema-libraries-for-haskell-321</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Update 2025-07-26: This review is obsolete. &lt;a href="https://dev.to/sshine/json-schema-in-haskell-using-autodocodec-35cg"&gt;Consider using AutoDoCodec&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="http://json-schema.org/" rel="noopener noreferrer"&gt;JSON Schema&lt;/a&gt; is a JSON format that describes JSON formats.&lt;/p&gt;

&lt;p&gt;It is mostly used to validate that a JSON value has the correct format, but it can also be used to generate random values that fit a schema definition. This may be useful for testing.&lt;/p&gt;

&lt;p&gt;The latest version of JSON Schema is called "Draft 2020-12", and the way to define a schema using this draft:&lt;br&gt;
&lt;/p&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;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://json-schema.org/draft/2020-12/schema"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&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;&lt;a href="http://json-schema.org/specification.html" rel="noopener noreferrer"&gt;Previous versions&lt;/a&gt; were called Draft-03 (2010), Draft-04 (2013), ... up to Draft-08 which was renamed into Draft 2019-09. When exploring Haskell libraries that handle JSON Schema definitions, they tend to have stalled on an earlier draft. There seems to be a pattern, and Juspay's &lt;a href="https://hackage.haskell.org/package/medea" rel="noopener noreferrer"&gt;medea&lt;/a&gt; package has already summarised what's going on in their README section &lt;a href="https://github.com/juspay/medea#why-medea" rel="noopener noreferrer"&gt;Why Medea?&lt;/a&gt;; where their answer goes in depth with some sober roasting, I'll summarise:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;The JSON Schema standard is complex&lt;/li&gt;
&lt;li&gt;It &lt;em&gt;[...]&lt;/em&gt; covers considerably more than simply validating JSON documents&lt;/li&gt;
&lt;li&gt;JSON Schema requires arbitrary URI resolution&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;This reminds me of Bloodhound's &lt;a href="https://github.com/bitemyapp/bloodhound#version-compatibility" rel="noopener noreferrer"&gt;support for ElasticSearch version 1 and 5&lt;/a&gt; (and why the library doesn't support version 6, 7, or 8): The complexity and constant release of new ElasticSearch API versions makes it difficult to make a typed library around it. I'm not sure exactly how to phrase it, but Haskell seems like a bad fit for this type of highly volatile interface. Adding recursive, unbounded network I/O as part of the validation makes a Haskeller less likely to pursue a full implementation.&lt;/p&gt;

&lt;p&gt;Never the less. I did a deep scan for the word "schema" on &lt;a href="https://hackage.haskell.org/packages/" rel="noopener noreferrer"&gt;https://hackage.haskell.org/packages/&lt;/a&gt; and procured the following list of JSON Schema-specific packages. They fall into one of two categories.&lt;/p&gt;

&lt;h2&gt;
  
  
  When a spec is too complex, a few things can happen
&lt;/h2&gt;

&lt;p&gt;First, there's the "we tried and gave up" category; they all have in common that they're attempts to make a working library for an early version of JSON Schema, and they all have in common that they're abandoned; such is the fate of open source sometimes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://hackage.haskell.org/package/aeson-schema" rel="noopener noreferrer"&gt;aeson-schema&lt;/a&gt;: Only Draft-03, &lt;a href="https://github.com/ocramz/aeson-schema#other-libraries" rel="noopener noreferrer"&gt;recommends hjsonschema&lt;/a&gt; instead.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hackage.haskell.org/package/hjsonschema" rel="noopener noreferrer"&gt;hjsonschema&lt;/a&gt;: Only Draft-04, &lt;a href="https://github.com/seagreen/hjsonschema#deprecation-notice" rel="noopener noreferrer"&gt;announced deprecated&lt;/a&gt; in an attempt to be too modular.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hackage.haskell.org/package/jsonschema-gen" rel="noopener noreferrer"&gt;jsonschema-gen&lt;/a&gt;, &lt;a href="https://hackage.haskell.org/package/jsons-to-schema" rel="noopener noreferrer"&gt;jsons-to-schema&lt;/a&gt;: Only Draft-04, neither does validate, generates schemas based on values, &lt;a href="https://github.com/garetht/jsons-to-schema/#future-plans" rel="noopener noreferrer"&gt;some limitations&lt;/a&gt; apply.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then there are the libraries that appear to be JSON Schema-related libraries judging by their name, but they are, in fact, all variations that explicitly do not attempt to support JSON Schema, but build something similar with more limited assumptions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://hackage.haskell.org/package/json-schema" rel="noopener noreferrer"&gt;json-schema&lt;/a&gt;: "It's haskell specific and has no relation to json-schema.org."&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hackage.haskell.org/package/aeson-schemas" rel="noopener noreferrer"&gt;aeson-schemas&lt;/a&gt;: Last updated in 2022! Type-safe schema language using Template Haskell. But it doesn't come with an option to load a JSON Schema .json file. So they're schemas in JSON, but not &lt;em&gt;JSON Schema&lt;/em&gt; schemas.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hackage.haskell.org/package/hschema-aeson" rel="noopener noreferrer"&gt;hschema-aeson&lt;/a&gt;: Last updated in 2022! A similar project that lets me specify schemas for Haskell data types and encode them as JSON. So they're schemas in JSON, but not &lt;em&gt;JSON Schema&lt;/em&gt; schemas.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hackage.haskell.org/package/schematic" rel="noopener noreferrer"&gt;schematic&lt;/a&gt;: Last updated in 2021. "It can be &lt;a href="https://github.com/typeable/schematic" rel="noopener noreferrer"&gt;thought of as a subset of &lt;em&gt;JSON Schema&lt;/em&gt;&lt;/a&gt;", "Schematic schemas can be exported to json-schema".&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hackage.haskell.org/package/medea" rel="noopener noreferrer"&gt;medea&lt;/a&gt;: Last updated in 2021. "Medea is a schema language for JSON document structure. It is similar to JSON Schema, but is designed to be simpler and more self-contained."&lt;/li&gt;
&lt;li&gt;&lt;em&gt;(&lt;a href="https://hackage.haskell.org/package/quick-schema" rel="noopener noreferrer"&gt;quick-schema&lt;/a&gt;: Last updated 2015. Minimalistic JSON schema language. Not maintained, and not exactly up to par.)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;If you wish to mostly support a 10-12 year old draft of JSON Schema using an unmaintained package that currently fails to build in a modern build environment, you have two options: aeson-schema, and hjsonschema. While one recommends the other, and the other is self-deprecated, I'm not completely sarcastic when I say that &lt;strong&gt;aeson-schema&lt;/strong&gt; could work. It appears well made, and you may want to support a super old JSON Schema definition before it got complex, or even extend it to a later draft.&lt;/p&gt;

&lt;p&gt;If you wish for &lt;em&gt;any&lt;/em&gt; coverage of Draft 2020-12, you're in bad luck. Haskellers simply gave up and wrote alternative JSON schema libraries. If you're not trying to release a JSON specification into the public domain, and you're just picking a good internal pipe language that supports JSON, any of &lt;strong&gt;aeson-schemas&lt;/strong&gt;, &lt;strong&gt;hschema-aeson&lt;/strong&gt;, &lt;strong&gt;schematic&lt;/strong&gt;, and &lt;strong&gt;medea&lt;/strong&gt; may be good choices. Or you may look at entirely different serialisation frameworks.&lt;/p&gt;

&lt;p&gt;While I cannot currently prioritise evaluating each of these, since I am in the process of releasing a specification for my new pet project, &lt;a href="https://github.com/json-flashcard" rel="noopener noreferrer"&gt;JSON Flashcard&lt;/a&gt;, and I do need for a specification format and not just a library, I'm trying my luck with &lt;strong&gt;schematic&lt;/strong&gt;, because it allows me to &lt;em&gt;export to JSON Schema&lt;/em&gt;, without trying to support it fully.&lt;/p&gt;

</description>
      <category>haskell</category>
      <category>json</category>
    </item>
    <item>
      <title>Has-style traits in Rust</title>
      <dc:creator>Simon Shine</dc:creator>
      <pubDate>Thu, 31 Mar 2022 06:37:01 +0000</pubDate>
      <link>https://dev.to/sshine/has-style-traits-in-rust-2ipf</link>
      <guid>https://dev.to/sshine/has-style-traits-in-rust-2ipf</guid>
      <description>&lt;p&gt;I recently experimented with &lt;a href="https://simonshine.dk/blog/rust-traits/"&gt;Rust trait instances on bounded generic types&lt;/a&gt;. This is the fancy way of saying &lt;code&gt;impl&amp;lt;T: SomeTrait&amp;gt; AnotherTrait for T&lt;/code&gt;, i.e. any number of &lt;code&gt;AnotherTrait&lt;/code&gt; instances created as a result of the presence of a &lt;code&gt;SomeTrait&lt;/code&gt; instance.&lt;/p&gt;

&lt;p&gt;Here is a similar but slightly more complicated example of that.&lt;/p&gt;

&lt;p&gt;I have a number of things that all have a common base set of properties. I'd like for the common base set to be described with one trait, and all the ways in which the things are different to be described with another trait that may depend on the common things.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="cd"&gt;/// First, a simple base struct:&lt;/span&gt;
&lt;span class="cd"&gt;///&lt;/span&gt;
&lt;span class="cd"&gt;/// It contains things that are common to a set of structs.&lt;/span&gt;
&lt;span class="nd"&gt;#[derive(Debug,&lt;/span&gt; &lt;span class="nd"&gt;Clone)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bar&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="cd"&gt;/// Then, a trait that describes it&lt;/span&gt;
&lt;span class="cd"&gt;///&lt;/span&gt;
&lt;span class="cd"&gt;/// This is an abstract interface over the same struct. It allows us to&lt;/span&gt;
&lt;span class="cd"&gt;/// access other structs as if they were `Base`, as long as they have&lt;/span&gt;
&lt;span class="cd"&gt;/// some way to access a `Base`, e.g. by having a field of type `Base`.&lt;/span&gt;
&lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;BaseTrait&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cd"&gt;/// Next, a trait that describes things that contain `Base`s&lt;/span&gt;
&lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;HasBase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cd"&gt;/// ...of which the simplest one is that `Base` has itself;&lt;/span&gt;
&lt;span class="cd"&gt;/// but to avoid infinite recursion, we `impl BaseTrait` for&lt;/span&gt;
&lt;span class="cd"&gt;/// `Base`.&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;BaseTrait&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.foo&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.bar&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cd"&gt;/// This is the crux:&lt;/span&gt;
&lt;span class="cd"&gt;///&lt;/span&gt;
&lt;span class="cd"&gt;/// A recursive trait instance that enables `BaseTrait`&lt;/span&gt;
&lt;span class="cd"&gt;/// calls on any thing that `HasBase` (i.e. has a `Base`).&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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;HasBase&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;BaseTrait&lt;/span&gt; &lt;span class="k"&gt;for&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;fn&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.get_base&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.get_base&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.bar&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="cd"&gt;/// Now, a `Thing` with a `Base`&lt;/span&gt;
&lt;span class="nd"&gt;#[derive(Debug,&lt;/span&gt; &lt;span class="nd"&gt;Clone)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Thing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Thing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Thing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;baz&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="cd"&gt;/// If `BaseTrait` had many functions, we'd save a lot of lines here.&lt;/span&gt;
&lt;span class="cd"&gt;///&lt;/span&gt;
&lt;span class="cd"&gt;/// This `impl HasBase for Thing` implies `impl BaseTrait for Thing`.&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;HasBase&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Thing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.base&lt;/span&gt;&lt;span class="nf"&gt;.clone&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="cd"&gt;/// So far, this is all great!&lt;/span&gt;
&lt;span class="cd"&gt;///&lt;/span&gt;
&lt;span class="cd"&gt;/// Now I want to make another trait that depends on BaseTrait&lt;/span&gt;
&lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;ExtensionTrait&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BaseTrait&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;foo_bar_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.bar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;bar_baz_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.bar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.baz&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cd"&gt;/// And what's more natural than give this trait to `Thing`&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ExtensionTrait&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Thing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.baz&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&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;some_thing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Thing&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="nn"&gt;Base&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;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;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"foo = {}, bar = {}, baz = {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;some_thing&lt;/span&gt;&lt;span class="nf"&gt;.foo&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;some_thing&lt;/span&gt;&lt;span class="nf"&gt;.bar&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;some_thing&lt;/span&gt;&lt;span class="nf"&gt;.baz&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"foo + bar = {}, bar + baz = {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;some_thing&lt;/span&gt;&lt;span class="nf"&gt;.foo_bar_sum&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;some_thing&lt;/span&gt;&lt;span class="nf"&gt;.bar_baz_sum&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;



</description>
      <category>rust</category>
    </item>
    <item>
      <title>Writing code on doors</title>
      <dc:creator>Simon Shine</dc:creator>
      <pubDate>Tue, 29 Mar 2022 18:18:06 +0000</pubDate>
      <link>https://dev.to/sshine/writing-code-on-doors-257e</link>
      <guid>https://dev.to/sshine/writing-code-on-doors-257e</guid>
      <description>&lt;p&gt;Did you ever see pseudo-code in job ads that looked like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;you&lt;/span&gt;&lt;span class="nf"&gt;.like_to_work&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;here&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="n"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="nf"&gt;.apply&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;and thought to yourself a bunch of things about variables not being in scope, and whether their actual code has utterly specific library functions? Did you ever try to design an authentic piece of pseudo-code and found how difficult it is to remain concise enough to get the point across, but also idiomatic enough to even care expressing this as code?&lt;/p&gt;

&lt;p&gt;At my local office, a sign was recently posted with instructions to lock the office.&lt;/p&gt;

&lt;p&gt;Shortly after, another sign followed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xoKn541u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/tgrlrVI.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xoKn541u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/tgrlrVI.png" alt="The code on these doors is highly ambiguous!" width="880" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Analysis
&lt;/h2&gt;

&lt;p&gt;Having policies listed on the door is a bit messy, but it gets the point across. Perhaps some of the mess can be reduced by analysing the process through which they ended up here, and produce a shorter version.&lt;/p&gt;

&lt;p&gt;Growing an office policy is an iterative process, and two separate methods of adding new rules were used: Adding to the paper with the existing policy, and hanging up new paper. The fact that a new rule is added with a pen is not actually a problem, but is the greatest feature of paper tech that we have yet to reinvent elsewhere.&lt;/p&gt;

&lt;p&gt;Both methods introduce redundancy: The hand-written "Turn off coffee machine" and all but one of the printed bullets are listed twice. Since the lists are short, it is easy to determine what the key point is: Start the dishwasher before weekends begin.&lt;/p&gt;

&lt;p&gt;Being new to Rust, I wanted to write the stupid pseudo-code version of this. My first iteration simply tried to reduce code duplication by making the weekend-dishwasher rule conditional:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Shutdown&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Office&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;leave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Local&lt;/span&gt;&lt;span class="o"&gt;&amp;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;self&lt;/span&gt;&lt;span class="nf"&gt;.lights&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Off&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;On&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.coffee_machine&lt;/span&gt;&lt;span class="nf"&gt;.stop&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;now&lt;/span&gt;&lt;span class="nf"&gt;.weekday&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nn"&gt;Weekday&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Fri&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.dishwasher&lt;/span&gt;&lt;span class="nf"&gt;.start&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;The basic thing I like about this code is that it looks like Rust, but doesn't go much into what a &lt;code&gt;Shutdown&lt;/code&gt; or an &lt;code&gt;Office&lt;/code&gt; actually is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ambiguity
&lt;/h2&gt;

&lt;p&gt;The lovely and sometimes frustrating thing about human language is that it leaves room for ambiguity. You can say multiple things at once, say different things to different people with one message, and leave room for interpretation.&lt;/p&gt;

&lt;p&gt;The moment you write human instructions as programming code, the ambiguity becomes glaringly apparent. Once I had read the instructions as programming code, I immediately thought, "Wait, what if Friday is a bank holiday?" I actually want a softer definition of weekend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Abusing ambiguity
&lt;/h2&gt;

&lt;p&gt;So wait...&lt;/p&gt;

&lt;p&gt;I'm starting the dishwasher on Fridays, no matter what, just because it's weekend? This seems like an X-Y problem. I know how dishwashers work. We don't want to accumulate smell over the weekend, and we like to tidy our workspace so that arriving to it gives you energy. But we also don't want to pointlessly start dishwashers on Fridays. What are the actual criteria?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Shutdown&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Office&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;leave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Local&lt;/span&gt;&lt;span class="o"&gt;&amp;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;self&lt;/span&gt;&lt;span class="nf"&gt;.lights&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Off&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;On&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.coffee_machine&lt;/span&gt;&lt;span class="nf"&gt;.stop&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;now&lt;/span&gt;&lt;span class="nf"&gt;.is_beginning_of_weekend&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.dishwasher&lt;/span&gt;&lt;span class="nf"&gt;.is_full&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.dishwasher&lt;/span&gt;&lt;span class="nf"&gt;.start&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;So wait...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We only start the dishwasher on Fridays if it's full?&lt;/li&gt;
&lt;li&gt;What if it's half-full, or it's full and isn't a Friday?&lt;/li&gt;
&lt;li&gt;What if the dishwasher has already started? Do we assume that &lt;code&gt;.start()&lt;/code&gt; is idempotent, do we stop the machine just to start it again, or do we busy-wait for the machine to stop just to start it a second time? It is Friday, after all!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A problem of joining the two pieces of paper was that they carried different implicit assumptions; joining them, the absence of any non-Friday dishwasher logic should not imply a hard rule against using the dishwasher on non-Fridays. We could be more explicit about overlapping event handlers, but that sounds like boilerplate code.&lt;/p&gt;

&lt;h2&gt;
  
  
  You start empty dishwashers?
&lt;/h2&gt;

&lt;p&gt;It was at this time that I changed the policy and let go of the "Dishwasher Friday" rule. I know, I know. Cleaning nazi! Party pooper! But really, as these rules are explicated further, I am sure that people will appreciate how rarely they actually have to start the dishwasher once they wield the powers of formal logic.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Let's not start empty dishwashers just because we can. And let's add soap.&lt;/li&gt;
&lt;li&gt;A lady came by and suggested that we also want to turn off the AC when we leave.&lt;/li&gt;
&lt;li&gt;Since we're programming, and Rust has sequential evaluation semantics, let's not turn off the lights and lock the door before we've performed the tasks that are better performed with unimpaired vision and being able to leave.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Shutdown&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Office&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;leave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.air_condition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Off&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.coffee_machine&lt;/span&gt;&lt;span class="nf"&gt;.stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.dish_washer&lt;/span&gt;&lt;span class="nf"&gt;.is_empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
           &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.dishwasher&lt;/span&gt;&lt;span class="nf"&gt;.is_running&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
           &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.dish_washer&lt;/span&gt;&lt;span class="nf"&gt;.is_complete&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.dish_washer&lt;/span&gt;&lt;span class="nf"&gt;.add_soap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.dish_washer&lt;/span&gt;&lt;span class="nf"&gt;.start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.lights&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Off&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;On&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;



</description>
      <category>rust</category>
    </item>
    <item>
      <title>Implement Rust trait for all types that have another trait</title>
      <dc:creator>Simon Shine</dc:creator>
      <pubDate>Tue, 15 Feb 2022 01:30:04 +0000</pubDate>
      <link>https://dev.to/sshine/implement-rust-trait-for-all-types-that-have-another-trait-2bbl</link>
      <guid>https://dev.to/sshine/implement-rust-trait-for-all-types-that-have-another-trait-2bbl</guid>
      <description>&lt;p&gt;Rust traits are powerful. A particularly neat thing you can do is implement a trait for several types at once. To give a simple example, a trait for squaring numbers mainly depends on multiplying them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;Square&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&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;We could implement this trait for a single type like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Square&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;self&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;But instead of adding &lt;code&gt;impl Square for ...&lt;/code&gt; for each type we want to &lt;code&gt;.square()&lt;/code&gt;, this &lt;code&gt;impl&lt;/code&gt; can be generalised to all types that &lt;code&gt;impl Mul&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Mul&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Output&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="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Copy&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Square&lt;/span&gt; &lt;span class="k"&gt;for&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;fn&lt;/span&gt; &lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;self&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;The key here is &lt;code&gt;impl&amp;lt;T: ...&amp;gt; Square for T { ... }&lt;/code&gt;, that is, &lt;code&gt;for T&lt;/code&gt;, the type that the &lt;code&gt;impl&lt;/code&gt; takes as parameter. When eventually the constraints on &lt;code&gt;T&lt;/code&gt; become too many, they can be moved to a &lt;code&gt;where&lt;/code&gt; clause:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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;Square&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt;
    &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Mul&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Output&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="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Copy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;square&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;self&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;Here's an example of &lt;code&gt;.square()&lt;/code&gt; being used for multiple types that already &lt;code&gt;impl Mul&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"{}, {}, {}, {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;10u8&lt;/span&gt;&lt;span class="nf"&gt;.square&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="mi"&gt;3u16&lt;/span&gt;&lt;span class="nf"&gt;.square&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="mi"&gt;4u32&lt;/span&gt;&lt;span class="nf"&gt;.square&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="mi"&gt;5u64&lt;/span&gt;&lt;span class="nf"&gt;.square&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;



</description>
      <category>rust</category>
      <category>types</category>
      <category>trait</category>
    </item>
    <item>
      <title>A Terraform DNS moment</title>
      <dc:creator>Simon Shine</dc:creator>
      <pubDate>Sat, 29 Jan 2022 02:53:40 +0000</pubDate>
      <link>https://dev.to/sshine/a-terraform-dns-moment-49m9</link>
      <guid>https://dev.to/sshine/a-terraform-dns-moment-49m9</guid>
      <description>&lt;p&gt;&lt;strong&gt;tl;dr:&lt;/strong&gt; I stuck the IPv4 and IPv6 address of my VPS into an SPF record field text.&lt;/p&gt;

&lt;p&gt;I manage my DNS with Terraform. This means that past domain registration and changing nameservers, I manage DNS with the Terraform command-line interface and git. Today I experienced a moment of happiness when two pieces of personal infrastructure fitted.&lt;/p&gt;

&lt;p&gt;The scenario: I'm setting up a send-only email server on a VPS for Gitea notifications. If this works out well, I will consider using it for a newsletter. Doing this on a commercial cloud VPS means that my server's IPv4 address gets a bad score. One way to improve on that score is to add an &lt;a href="http://www.open-spf.org/SPF_Record_Syntax/"&gt;SPF record&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For my personal email hosted at FastMail, the SPF record is simpler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ dig +short TXT simonshine.dk
"v=spf1 include:spf.messagingengine.com -all"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Their SPF record contains a bunch of &lt;code&gt;ip4:...&lt;/code&gt; rules.&lt;/p&gt;

&lt;p&gt;To let the world know that I'm sending emails from my VPS, I simply list its IP address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "digitalocean_record" "spf_example_com" {
  domain = digitalocean_domain.example_com.name
  type   = "TXT"
  name   = "@"
  value  = "v=spf1 ${format("ip4:%s ip6:%s",
    digitalocean_droplet.example_droplet.ipv4_address,
    digitalocean_droplet.example_droplet.ipv6_address)} -all"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This encourages receivers of emails from this domain to reject the email if it didn't originate from this server. The part that got me blogging is that I didn't hardcode the IP addresses of my server, but rather, the value was taken from the deployment configuration of the concrete server.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>dns</category>
      <category>infrastructure</category>
      <category>digitalocean</category>
    </item>
    <item>
      <title>Fiat currencies are not one thing</title>
      <dc:creator>Simon Shine</dc:creator>
      <pubDate>Fri, 10 Dec 2021 12:10:33 +0000</pubDate>
      <link>https://dev.to/sshine/fiat-currencies-are-not-one-thing-2obh</link>
      <guid>https://dev.to/sshine/fiat-currencies-are-not-one-thing-2obh</guid>
      <description>&lt;p&gt;When we discuss the potential future usefulness of cryptocurrency, we compare against existing use-cases of fiat currency. Given a particular fiat currency -- its name, its unit, and its exchange rate -- our language enforces us to think of it as one currency.&lt;/p&gt;

&lt;p&gt;This is a misconception that will lead to poorly designed alternatives with strong, but unaligned monetary properties at the wrong time. If we don't think of a fiat currency as many currencies within one brand, then any attempt to re-model those use-cases with cryptocurrencies will come short of being practically useful.&lt;/p&gt;

&lt;p&gt;The classic shapes of fiat currencies are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Physical cash money&lt;/li&gt;
&lt;li&gt;Credit card money&lt;/li&gt;
&lt;li&gt;Bank loan money&lt;/li&gt;
&lt;li&gt;Company money&lt;/li&gt;
&lt;li&gt;Point system money&lt;/li&gt;
&lt;li&gt;New digital cash money&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The reason why thinking of these as separate currencies within the same brand is that they do not have the same monetary properties, or even the same purposes, at all. And if you consider the cost of exchanging one shape for another as part of the exchange rate, they don't even share that.&lt;/p&gt;

&lt;p&gt;When Bitcoin were still getting popular, the following &lt;a href="https://bitcoinmagazine.com/culture/monetary-properties-of-bitcoin"&gt;six historic monetary properties&lt;/a&gt; were used to justify being "real money":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scarcity (has limited supply)&lt;/li&gt;
&lt;li&gt;Durability (doesn't break)&lt;/li&gt;
&lt;li&gt;Acceptability (can pay with)&lt;/li&gt;
&lt;li&gt;Portability (can carry)&lt;/li&gt;
&lt;li&gt;Divisibility (can buy big/small things)&lt;/li&gt;
&lt;li&gt;Fungibility (all coins are the same)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now cryptocurrencies are immensely popular, but still mostly useless. While these monetary properties have gradually become true for money historically, cryptocurrencies generally lack some of them; in particular, mainstream acceptability and practical durability (&lt;a href="https://vitalik.ca/general/2021/01/11/recovery.html"&gt;recover lost funds&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;We could write that off as work in progress. But why bother changing currencies if all you get is the same, but with fewer consumer guarantees (e.g. conditional transaction reversibility) and a worse user experience?&lt;/p&gt;

&lt;p&gt;Perhaps, like not all historic money had all desirable monetary properties in the same degree, or at all, the newer monetary properties of cryptocurrencies have yet to be understood and valued because they still fail basic usability.&lt;/p&gt;

</description>
      <category>crypto</category>
      <category>cryptocurrency</category>
      <category>economics</category>
    </item>
    <item>
      <title>jq hack #2: curl'ing the right binary on GitHub</title>
      <dc:creator>Simon Shine</dc:creator>
      <pubDate>Sat, 04 Dec 2021 17:53:45 +0000</pubDate>
      <link>https://dev.to/sshine/jq-hack-2-curling-the-right-binary-on-github-59n2</link>
      <guid>https://dev.to/sshine/jq-hack-2-curling-the-right-binary-on-github-59n2</guid>
      <description>&lt;p&gt;Something I see quite often is a combination of &lt;code&gt;grep&lt;/code&gt; and &lt;code&gt;cut&lt;/code&gt; to extract URLs from JSON objects, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -s https://api.github.com/repos/go-gitea/gitea/releases/latest \
  | grep browser_download_url \
  | cut -d '"' -f 4 \
  | grep '\linux-amd64$' \
  | wget -i -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is neat because &lt;code&gt;grep&lt;/code&gt; and &lt;code&gt;cut&lt;/code&gt; are almost always available on Linux. And the approach works for all kinds of plaintext documents with a semi-expectable format, not just JSON, so the approach is worth keeping around. But for JSON in particular, this approach is unnecessarily fragile because of a few unreasonable assumptions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Whitespace formatting: If, for example, GitHub decides to start compacting their JSON output, this script would stop working, since it assumes "browser_download_url" is on a separate line with its URL value.&lt;/li&gt;
&lt;li&gt;JSON escape sequences: If, for example, the queried output contains JSON escape sequences, then those will not be evaluated, so &lt;code&gt;\u....&lt;/code&gt; would become six characters rather than one Unicode character.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a &lt;code&gt;jq&lt;/code&gt; version that is stable across legal JSON syntax variation, one could do this instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -s https://api.github.com/repos/go-gitea/gitea/releases/latest \
  | jq -r '.assets[]
           | .browser_download_url
           | select(endswith("linux-amd64"))'
https://github.com/go-gitea/gitea/releases/download/v1.15.7/gitea-1.15.7-linux-amd64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the tl;dr.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explaining how this query works
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;curl -s&lt;/code&gt; only prints the file contents, not debug information about the download.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;jq -r&lt;/code&gt; outputs plaintext instead of JSON &lt;em&gt;if and only if&lt;/em&gt; the object you're printing is a JSON string.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.assets[]&lt;/code&gt; enters the "assets" sub-field and splits the list it contains into multiple results,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.browser_download_url&lt;/code&gt; reduces each of those multiple results to its sub-field,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;select(endswith("linux-amd64"))&lt;/code&gt; removes results that don't match.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a variation of the query that produce the same result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -s https://api.github.com/repos/go-gitea/gitea/releases/latest \
  | jq -r '.assets
           | map(.browser_download_url
                 | select(endswith("linux-amd64")))
           | .[]'
https://github.com/go-gitea/gitea/releases/download/v1.15.7/gitea-1.15.7-linux-amd64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference is that here, &lt;code&gt;.assets&lt;/code&gt; instead of &lt;code&gt;.assets[]&lt;/code&gt; does not split the sub-field into multiple results. Instead, &lt;code&gt;map(...)&lt;/code&gt; runs &lt;code&gt;...&lt;/code&gt; on each element in the one result being a list (and not the many results being the list's elements). &lt;/p&gt;

&lt;p&gt;The query itself in both cases is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.browser_download_url | select(endswith("linux-amd64"))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which first turns each list element into its sub-field, being only the URL, and then removes that URL from the inside of the list if it isn't the right one. Lastly, &lt;code&gt;.[]&lt;/code&gt; takes the list of remaining URLs and turns them into multiple string results.&lt;/p&gt;

&lt;p&gt;This &lt;code&gt;map()&lt;/code&gt; variation is worth considering because it also lets us filter based on criteria made on the full asset object, and not just the URL itself. For example, to remove any uploads from the result that were not made by the GiteaBot user,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -s https://api.github.com/repos/go-gitea/gitea/releases/latest \
  | jq -r '.assets | map(select(.uploader.login == "GiteaBot")
                         | .browser_download_url
                         | select(endswith("linux-amd64")))
                   | .[]'
https://github.com/go-gitea/gitea/releases/download/v1.15.7/gitea-1.15.7-linux-amd64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Exploring a big JSON document with jq/less
&lt;/h2&gt;

&lt;p&gt;In the process of deriving the final &lt;code&gt;jq&lt;/code&gt; query, I relied on using &lt;a href="https://dev.to/sshine/jq-hack-1-colored-less-3fo3"&gt;&lt;code&gt;jq&lt;/code&gt; and &lt;code&gt;less&lt;/code&gt; as an ad-hoc JSON browser&lt;/a&gt; using the following steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -s https://api.github.com/repos/go-gitea/gitea/releases/latest \
  | jq -C . | less -R
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thought: So there's an &lt;code&gt;.assets&lt;/code&gt; list, and &lt;code&gt;.browser_download_url&lt;/code&gt; is a direct child field in each underlying asset object... how many elements does this &lt;code&gt;.assets&lt;/code&gt; have?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -s https://api.github.com/repos/go-gitea/gitea/releases/latest \
  | jq '.assets | length'
72
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thought: And how many assets are actually related to "linux-amd64"?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -s https://api.github.com/repos/go-gitea/gitea/releases/latest \
  | jq -r '.assets[] | .browser_download_url | select(contains("linux-amd64"))'
https://github.com/go-gitea/gitea/releases/download/v1.15.7/gitea-1.15.7-linux-amd64
https://github.com/go-gitea/gitea/releases/download/v1.15.7/gitea-1.15.7-linux-amd64.asc
https://github.com/go-gitea/gitea/releases/download/v1.15.7/gitea-1.15.7-linux-amd64.sha256
https://github.com/go-gitea/gitea/releases/download/v1.15.7/gitea-1.15.7-linux-amd64.xz
https://github.com/go-gitea/gitea/releases/download/v1.15.7/gitea-1.15.7-linux-amd64.xz.asc
https://github.com/go-gitea/gitea/releases/download/v1.15.7/gitea-1.15.7-linux-amd64.xz.sha256
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>jq</category>
      <category>github</category>
      <category>curl</category>
      <category>linux</category>
    </item>
    <item>
      <title>Hvorfor må jeg ikke eje en svensker?</title>
      <dc:creator>Simon Shine</dc:creator>
      <pubDate>Sun, 28 Nov 2021 16:04:13 +0000</pubDate>
      <link>https://dev.to/sshine/hvorfor-ma-jeg-ikke-eje-en-svensker-1094</link>
      <guid>https://dev.to/sshine/hvorfor-ma-jeg-ikke-eje-en-svensker-1094</guid>
      <description>&lt;p&gt;&lt;em&gt;Written January 1, 2003&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The following essay is an adaptation of a letter titled &lt;em&gt;Why Can’t I Own a Canadian?&lt;/em&gt; sent to the religious radio host Dr. Laura Schlessinger in October 2002, as a response to a statement that homosexuality is an abomination according to Leviticus 18:22, and cannot be condoned under any circumstance. This Danish adaptation was originally posted by me to the &lt;a href="https://ateist.dk/"&gt;Atheist Society of Denmark&lt;/a&gt; web forums, but was later lost from there and copied without attribution.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Kære kristen&lt;/p&gt;

&lt;p&gt;Tak fordi du gør så meget for at prædike Guds ord. Jeg har lært en masse ved at gå i kirke, og jeg prøver at dele min viden med så mange som muligt. Når nogen for eksempel forsvarer homoseksualitet henviser jeg dem bare til 3. Mosebog kapitel 18 vers 22 der siger at du ikke må have samleje med en mand, som man har samleje med en kvinde. Det er en vederstyggelighed. Diskussion slut. Jeg har dog nogle spørgsmål om nogle andre love og hvordan man bør følge dem:&lt;/p&gt;

&lt;p&gt;Når jeg brænder en tyr ved alteret, ved jeg ifølge 3. Mosebog kapitel 1 vers 9 at det skaber en liflig duft for Herren. Problemet er imidlertid mine naboer, der siger at de ikke kan lide lugten. Skal jeg dræbe dem som Bibelen foreslår?&lt;/p&gt;

&lt;p&gt;Jeg har overvejet at sælge min datter væk som slave som stadfæstet i 2. Mosebog kapitel 21 vers 7: Når en mand sælger sin datter som trælkvinde, skal hun ikke frigives på samme måde som trællene. Hvad ville en fair pris være for hende nu om dage?&lt;/p&gt;

&lt;p&gt;Jeg ved at jeg ikke må have kontakt med kvinder i deres menstruationsperiode som skrevet i 3. Mosebog kapitel 19-24 (Hun er uren i syv dage, hvis jeg rører hende eller ved noget hun har rørt skal jeg vaske mig og er uren indtil om aftenen, alt hvad hun rører ved er urent og jeg må heller ikke have samleje med hende.) Problemet her er bare, hvordan ved jeg om kvinder menstruerer? Jeg har prøvet at spørge, men de fleste kvinder bliver fornærmede.&lt;/p&gt;

&lt;p&gt;Jeg må ifølge 3. Mosebog kapitel 25 vers 44 godt eje slaver: De trælle og trælkvinder, du vil have, kan I købe af folkene omkring jer. Både mænd og kvinder, hvis bare de er købt fra nabolande. En af mine venner siger at det er rigtig nok for polakker og pakistanere, men ikke for svenskere. Kan du uddybe? Hvorfor må jeg ikke eje en svensker?&lt;/p&gt;

&lt;p&gt;Min nabo arbejder i Aldi, der som måske bekendt har åbent om søndagen.. 2. Mosebog kapitel 35 vers 2 siger klart at I seks dage må der udføres arbejde, men den syvende dag skal være hellig for jer; I skal holde fuldstændig hvile for Herren. Enhver, der udfører arbejde på den dag, skal lide døden. Er jeg moralsk forpligtet til at dræbe ham og alle andre der arbejder i Aldi? Yderligere siger den at I ikke må tænde ild på sabbatsdagen, hvor end I bor, og jeg ved med sikkerhed at min nabo laver varm mad om søndagen.&lt;/p&gt;

&lt;p&gt;En af mine venner siger at selvom det er en vederstyggelighed at spise skaldyr (3. Mosebog kapitel 11 vers 10), er det en mindre vederstyggelighed end at være homoseksuel. Jeg er ikke enig. Kan du afgøre dette for os?&lt;/p&gt;

&lt;p&gt;I 3. Mosebog kapitel 21 vers 20 står at pukkelryggede, dværge, folk med dårligt syn eller folk der har fnat eller beskadigede testikler ikke må nærme sig Guds alter. Jeg må indrømme at mit syn ikke er helt fejlfrit, men er der plads til lidt albuerum her? Ifølge vers 19 må mænd med brækkede arme heller ikke, så jeg har bedt min bror om at blive hjemme fra kirke de sidste tre måneder.&lt;/p&gt;

&lt;p&gt;De fleste af mine mandlige venner får deres hår klippet, inklusiv ved deres tindinger, selvom dette eksplicit er forbudt i 3. Mosebog: I må ikke klippe håret kort, og du må ikke studse skægget. Hvordan skal mine venner dræbes?&lt;/p&gt;

&lt;p&gt;Jeg ved fra 3. Mosebog kapitel 11 vers 6-8 at jeg ikke må røre ved en død gris’ hud, men må jeg godt spille fodbold alligevel hvis jeg bærer handsker?&lt;/p&gt;

&lt;p&gt;Min onkel har en bondegård. Han overskrider 3. Mosebog kapitel 19 vers 19 ved at tilså to slags korn på sin mark, og det gør hans kone også ved at bære tøj, der er vævet af to slags garn. Deres søn bander også en del. Er det virkelig nødvendigt at besvære sig med at samle hele byen for at stene dem? (3. Mosebog kapitel 24 vers 10-16) Kunne vi ikke bare brænde dem til døde som et andet privat familieforetage­nde ligesom vi gør med folk der har samleje med deres svigermoder? (3. Mosebog kapitel 20 vers 14)&lt;/p&gt;

&lt;p&gt;Jeg ved at du som kristen har studeret disse og mange andre problemstilling­er som Bibelen bringer os, og jeg er sikker på at du kan hjælpe. Tak for at minde os om at Guds ord er evigt og uforanderligt.&lt;/p&gt;

&lt;p&gt;Hilsen Simon&lt;/p&gt;

</description>
      <category>christianity</category>
    </item>
    <item>
      <title>Why rewriting version control history matters</title>
      <dc:creator>Simon Shine</dc:creator>
      <pubDate>Thu, 25 Nov 2021 22:54:41 +0000</pubDate>
      <link>https://dev.to/sshine/why-rewriting-version-control-history-matters-4nn1</link>
      <guid>https://dev.to/sshine/why-rewriting-version-control-history-matters-4nn1</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Life is understood backwards, but must be lived forwards.&lt;br&gt;
--- Søren Kierkegaard&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  A summary on good commit messages
&lt;/h2&gt;

&lt;p&gt;Because version control systems, like git and GitHub, are at the heart of modern software development workflows, most workflows are heavily impacted by and defined in terms of how to operate and cooperate around version control.&lt;/p&gt;

&lt;p&gt;Making use of issue trackers, pull requests, review tools, and commit messages, there are many right ways to distribute the communication that revolves around a change in source code. So...&lt;/p&gt;

&lt;p&gt;Why good commit messages?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code is read much more often than it is written. As &lt;a href="https://devblogs.microsoft.com/oldnewthing/20070406-00/?p=27343"&gt;Raymond Chen&lt;/a&gt; says:&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Even if you don't intend anybody else to read your code, there's still a very good chance that somebody will have to stare at your code and figure out what it does: That person is probably going to be you, twelve months from now.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;The same reason extends to commit messages. As &lt;a href="https://chris.beams.io/posts/git-commit/"&gt;Chris Beans&lt;/a&gt; says:&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;... a well-crafted Git commit message is the best way to communicate context about a change to fellow developers (and indeed to their future selves). A diff will tell you&lt;/em&gt; what &lt;em&gt;changed, but only the commit message can properly tell you&lt;/em&gt; why.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;When an entire commit message consists of messages like "wip", "small fix", or "added code", &lt;a href="http://who-t.blogspot.com/2009/12/on-commit-messages.html"&gt;Peter Hutterer&lt;/a&gt; suggests this is due to a lack of training in reading code:&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;If you haven't given much thought to what makes a great Git commit message, it may be the case that you haven't spent much time using &lt;code&gt;git log&lt;/code&gt; and related tools. There is a vicious cycle here: because the commit history is unstructured and inconsistent, one doesn't spend much time using or taking care of it. And because it doesn't get used or taken care of, it remains unstructured and inconsistent.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The cost of rewriting under collaboration
&lt;/h2&gt;

&lt;p&gt;When discovering the solution to a problem, the explored path is rarely the shortest path. Placing good explanations in git history makes it ideal for being read, but there are several costs when one has to "go back" and place them while at the same time collaborate with others:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cost of learning new tools:&lt;/strong&gt; Rewriting git history requires some of the more difficult git commands, but there are simple approaches, too:

&lt;ul&gt;
&lt;li&gt;GitHub lets you &lt;a href="https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/about-pull-request-merges#squash-and-merge-your-pull-request-commits"&gt;squash pull requests&lt;/a&gt;; in the web UI, you can squash all commits into a single one and edit the commit message in a textarea. This works best when your pull requests are small.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git commit --fixup&lt;/code&gt; and &lt;code&gt;git rebase -i --autosquash&lt;/code&gt; are easy to learn and use. Read the &lt;a href="https://fle.github.io/git-tip-keep-your-branch-clean-with-fixup-and-autosquash.html"&gt;&lt;code&gt;--fixup &amp;amp; --autosquash&lt;/code&gt; guide&lt;/a&gt; by Florent Lebreton.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git reset --soft&lt;/code&gt; lets you re-commit everything anew. Read &lt;a href="https://medium.com/magnetis-backstage/git-basics-rewriting-a-branchs-commit-history-from-scratch-7bc966716d8b"&gt;Git Basics: Rewriting a branch's commit history from scratch&lt;/a&gt; by Igor Marques da Silva.&lt;/li&gt;
&lt;li&gt;You can always create a local copy of a branch before attempting to rewrite history. You can even view the history of your copy (using &lt;code&gt;git log &amp;lt;branch&amp;gt;&lt;/code&gt; and &lt;code&gt;git show &amp;lt;hash&amp;gt;&lt;/code&gt;) while simultaneously rebuilding the history (using &lt;code&gt;git add -p&lt;/code&gt; and &lt;code&gt;git commit -v&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost of wording:&lt;/strong&gt; The time and patience to write a good explanation, and the expectation that it will eventually be read. Writing messages to your future self can work as a legitimate motivator.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cost of merge conflicts:&lt;/strong&gt; When rewriting history and force-pushing to a remote branch, this forces collaborators on that branch out of sync by removing conflict-free merge paths, and it risks overwriting contributions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clearly, &lt;strong&gt;&lt;code&gt;push --force&lt;/code&gt; is bad, mm'kay?!&lt;/strong&gt; Fortunately, &lt;a href="https://blog.developer.atlassian.com/force-with-lease/"&gt;Steve Smith&lt;/a&gt; has something to say:&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The problem here is that when doing a force push Bob doesn’t know why his changes have been rejected, so he assumes that it’s due to the rebase, not due to Alice’s changes. This is why &lt;code&gt;--force&lt;/code&gt; on shared branches is an absolute no-no; and with the central-repository workflow any branch can potentially be shared.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;But &lt;code&gt;--force&lt;/code&gt; has a lesser-known sibling that partially protects against damaging forced updates; this is &lt;code&gt;--force-with-lease&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What &lt;code&gt;--force-with-lease&lt;/code&gt; does is refuse to update a branch unless it is the state that we expect; i.e. nobody has updated the branch upstream. In practice this works by checking that the upstream ref is what we expect, because refs are hashes, and implicitly encode the chain of parents into their value.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Even when your collaborator is only reviewing or evaluating code, and not pushing changes, a merge conflict caused by force-pushing can cause extra time spent syncing. If this bothers a collaborator, perhaps rewriting history can happen only before and after their role is fulfilled.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The explorative path might truthfully involve a lot of "small fix", "trying", and "oops" steps, but these are not interesting in 6 months. We may be interested in more than perfect hindsight, so leaving a trail of comments in an issue tracker, in a pull request thread, or in a code review is a way to deepen the understanding.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to write Good Commit Messages: Quick Reference
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://chris.beams.io/posts/git-commit/"&gt;How to write a git commit message&lt;/a&gt;, by Chris Beams, focuses on the formatting:&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;Separate subject from body with a blank line&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Limit the subject line to 50 characters,&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Capitalize the subject line&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Do not end the subject line with a period&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Use the imperative mood in the subject line&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Wrap the body at 72 characters&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Use the body to explain what and why vs. how&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://who-t.blogspot.com/2009/12/on-commit-messages.html"&gt;On commit messages&lt;/a&gt;, by Peter Hutterer, focuses on the &lt;em&gt;what&lt;/em&gt; and the &lt;em&gt;why&lt;/em&gt;:&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;Why is it necessary? It may fix a bug, it may add a feature, it may improve performance, reliabilty, stability, or just be a change for the sake of correctness.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;How does it address the issue? For short obvious patches this part can be omitted, but it should be a high level description of what the approach was.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;What effects does the patch have? (In addition to the obvious ones, this may include benchmarks, side effects, etc.)&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Some real-world examples
&lt;/h2&gt;

&lt;p&gt;Imagine receiving a pull request with the following commits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;commit edc9ff5febb698a05f68650d6d58828202c24daf
Author: John Doe &amp;lt;john.doe@example.com&amp;gt;
Date:   Mon Nov 22 16:23:24 2021 +0100

    WIP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This message is clearly a working title that should be rephrased when the author is comfortable. Sometimes you want to commit and push half-done work, and sometimes you want to wait with providing a good explanation.&lt;/p&gt;

&lt;p&gt;When collaborating, establish a standard way of saying "don't do X yet," where X could be &lt;em&gt;look at&lt;/em&gt;, &lt;em&gt;review&lt;/em&gt;, &lt;em&gt;merge&lt;/em&gt;, &lt;em&gt;contribute to&lt;/em&gt;, or any other activity that creates potential conflicts between contributors. Typically the word "WIP" means that.&lt;/p&gt;

&lt;p&gt;This warrants git history rewriting before merging.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;commit 11f8a068bec49abdddd7bdf203b77ecbfe48451f
Author: John Doe &amp;lt;john.doe@example.com&amp;gt;
Date:   Thu Nov 4 10:19:40 2021 +0100

    Added cronjob
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Added &lt;em&gt;what&lt;/em&gt; cronjob, and &lt;em&gt;why&lt;/em&gt;?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;commit da8362e0b43ea3b18013f9245a614cf00ce43b60
Author: John Doe &amp;lt;john.doe@example.com&amp;gt;
Date:   Thu Nov 4 12:49:43 2021 +0100

    fixup! Added cronjob
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The author is signalling that they intend to squash the change. With this intent in mind, this clearly warrants git history rewriting before merging.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;commit 57e0b283f69c576db48003dc1ec3b2f492f79540
Author: John Doe &amp;lt;john.doe@example.com&amp;gt;
Date:   Mon Nov 22 14:30:12 2021 +0100

    small fix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This commit should either be squashed into some other commit, or explain what small fix this is. This either warrants rewriting the commit message, or squashing the commit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;commit 438cc1ce87dec47081b75d4cbd6ce4abca40b8c0
Author: John Doe &amp;lt;john.doe@example.com&amp;gt;
Date:   Thu Oct 7 19:42:13 2021 +0200

    Added indexed "cachedStatus" field on KnowledgeNodeStore which is automatically filled with the value of KnowledgeNode-&amp;gt;data['status']['state']. This might help the speed of common KnowledgeNodeQuery calls. Migration included that needs to run before we can remove the old filtering logic in KnowledgeNodeQuery.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, that is a 308-character subject field. Besides coming up with a short, descriptive title, and arguably word-wrapping, this commit message does detail &lt;em&gt;what&lt;/em&gt; and &lt;em&gt;why&lt;/em&gt; rather than &lt;em&gt;how&lt;/em&gt;. The included migration that is mentioned could be split into a separate commit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IsHN149T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lkoxkebkg7dj29ovzrxc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IsHN149T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lkoxkebkg7dj29ovzrxc.png" alt=":(" width="648" height="666"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>codereview</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
