{
    "archive_path": "archive/1732743678.143301",
    "base_url": "norikitech.com/posts/functional-affirmations",
    "basename": "",
    "bookmarked_date": "2024-11-27 21:41",
    "canonical": {
        "archive_org_path": "https://web.archive.org/web/norikitech.com/posts/functional-affirmations",
        "dom_path": "output.html",
        "favicon_path": "favicon.ico",
        "git_path": "git/",
        "google_favicon_path": "https://www.google.com/s2/favicons?domain=norikitech.com",
        "headers_path": "headers.json",
        "htmltotext_path": "htmltotext.txt",
        "index_path": "index.html",
        "media_path": "media/",
        "mercury_path": "mercury/content.html",
        "pdf_path": "output.pdf",
        "readability_path": "readability/content.html",
        "screenshot_path": "screenshot.png",
        "singlefile_path": "singlefile.html",
        "warc_path": "warc/",
        "wget_path": null
    },
    "domain": "norikitech.com",
    "downloaded_at": "2024-11-27T21:41:19.574913+00:00",
    "downloaded_datestr": "2024-11-27 21:41",
    "extension": "",
    "hash": "X8GT7YTY0PPPQTB43BGP",
    "history": {
        "archive_org": [
            {
                "cmd": [
                    "/usr/bin/curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--head",
                    "--max-time",
                    "60",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 ArchiveBox/{VERSION} (+https://github.com/ArchiveBox/ArchiveBox/)",
                    "https://web.archive.org/save/https://norikitech.com/posts/functional-affirmations/"
                ],
                "cmd_version": "8.10.1",
                "end_ts": "2024-11-27T21:42:19.031759+00:00",
                "index_texts": null,
                "output": "https://web.archive.org/web/20241127214207/https://norikitech.com/posts/functional-affirmations/",
                "pwd": "/data/archive/1732743678.143301",
                "schema": "ArchiveResult",
                "start_ts": "2024-11-27T21:41:52.872875+00:00",
                "status": "succeeded"
            }
        ],
        "dom": [
            {
                "cmd": [
                    "/usr/bin/chromium-browser",
                    "--disable-features=DarkMode",
                    "--run-all-compositor-stages-before-draw",
                    "--hide-scrollbars",
                    "--autoplay-policy=no-user-gesture-required",
                    "--no-first-run",
                    "--use-fake-ui-for-media-stream",
                    "--use-fake-device-for-media-stream",
                    "--simulate-outdated-no-au='Tue, 31 Dec 2099 23:59:59 GMT'",
                    "--headless=new",
                    "--no-sandbox",
                    "--no-zygote",
                    "--disable-dev-shm-usage",
                    "--disable-software-rasterizer",
                    "--disable-sync",
                    "--window-size=1440,2000",
                    "--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 ArchiveBox/{VERSION} (+https://github.com/ArchiveBox/ArchiveBox/)",
                    "--user-data-dir=/data/personas/Default/chrome_profile",
                    "--profile-directory=Default",
                    "--dump-dom",
                    "https://norikitech.com/posts/functional-affirmations/"
                ],
                "cmd_version": "131.0.6778",
                "end_ts": "2024-11-27T21:41:29.627884+00:00",
                "index_texts": null,
                "output": "output.html",
                "pwd": "/data/archive/1732743678.143301",
                "schema": "ArchiveResult",
                "start_ts": "2024-11-27T21:41:25.348476+00:00",
                "status": "succeeded"
            }
        ],
        "favicon": [
            {
                "cmd": [
                    "/usr/bin/curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--max-time",
                    "60",
                    "--output",
                    "favicon.ico",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 ArchiveBox/{VERSION} (+https://github.com/ArchiveBox/ArchiveBox/)",
                    "https://www.google.com/s2/favicons?domain=norikitech.com"
                ],
                "cmd_version": "8.10.1",
                "end_ts": "2024-11-27T21:41:20.256682+00:00",
                "index_texts": null,
                "output": "favicon.ico",
                "pwd": "/data/archive/1732743678.143301",
                "schema": "ArchiveResult",
                "start_ts": "2024-11-27T21:41:19.941842+00:00",
                "status": "succeeded"
            }
        ],
        "git": [],
        "headers": [
            {
                "cmd": [
                    "/usr/bin/curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--head",
                    "--max-time",
                    "60",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 ArchiveBox/{VERSION} (+https://github.com/ArchiveBox/ArchiveBox/)",
                    "https://norikitech.com/posts/functional-affirmations/"
                ],
                "cmd_version": "8.10.1",
                "end_ts": "2024-11-27T21:41:20.819234+00:00",
                "index_texts": null,
                "output": "headers.json",
                "pwd": "/data/archive/1732743678.143301",
                "schema": "ArchiveResult",
                "start_ts": "2024-11-27T21:41:20.288293+00:00",
                "status": "succeeded"
            }
        ],
        "htmltotext": [
            {
                "cmd": [
                    "(internal) archivebox.extractors.htmltotext",
                    "./{singlefile,dom}.html"
                ],
                "cmd_version": "0.8.5rc51",
                "end_ts": "2024-11-27T21:41:45.157954+00:00",
                "index_texts": [
                    "Functional programming self-affirmations - NorikiTech (/theme.css) (/favicon.ico) (https://assets.mailerlite.com/css/universal.css)  (/) NorikiTech  (/projects/) Projects (/posts/) Posts (/tags/) Tags   Functional programming self-affirmations  Published November 24, 2024 Dmitrii Kovanikov writes posts about enterprise software development and functional programming, both entertaining and serious. I never used a functional programming language (such as Haskell or OCaml) at work, but several ideas from functional programming have become popular in mainstream general-purpose programming languages such as Swift (which I mostly write). Often even a partially functional approach produces simpler code that\u2019s easy to understand and quick to write and maintain. I\u2019m always looking for good ideas I can adopt to write better code, whatever their source. Dmitrii recently (https://x.com/ChShersh/status/1859922977208598610) posted a tweet (now also (https://bsky.app/profile/chshersh.com/post/3lbjuzgcf6k2w) reposted to Bluesky ) titled \u201cFunctional programming self-affirmations\u201d that listed five items: Parse, don\u2019t validate Make illegal states unrepresentable Errors as values Functional core, imperative shell Smart constructor  These got me interested, but as usual, the problem with such distilled mantras is that you need a lot of context to understand and use them. What does it mean to have a \u201csmart constructor\u201d? In the comments  Dmitrii himself and other people added links to suggested reading. Let\u2019s go over each item and uncover what it means. 1. Parse, don\u2019t validate The text that introduced this notion is (https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/) this blog post with the same name written by Alexis King. It also covers the next notion so we\u2019ll come back to it later. It gives a much better explanation than my second-hand version could ever do. For me the two standout ideas were: While validation establishes correctness, parsing enriches the data and adds knowledge we can build upon, and does that early on the way from less-structured to more-structured data. In my own work I describe it as \u201cbuilding a solid foundation\u201d meaning that you can confidently build higher-level abstractions when the lower-level abstractions are leak-proof, with correctness likely enforced by the compiler. The notion of shotgun parsing (a mix of input validation and parsing that tries to capture all the \u201cbad\u201d cases \u2014 an antipattern, because it doesn\u2019t work) defined in the paper \u201c(https://langsec.org/papers/langsec-cwes-secdev2016.pdf) The Seven Turrets of Babel: A Taxonomy of LangSec Errors and How to Expunge Them \u201d. It is exactly the same situation as writing tests: the tests cannot prove the absence of bugs, only that certain things are correct. To come closer to proven correctness, illegal states must be forced out of the program, usually with robust type definitions.  Which neatly leads us to the next point\u2026 2. Make illegal states unrepresentable This is an idea that I see talked about fairly often even in \u201cregular\u201d commercial programming. However, in my experience, not many people are willing to go all the way to achieve it. The post I linked above talks about this notion in its second half. I define it to mean \u201cyour logic and types (where applicable) are designed in such a way as to make it physically impossible to create a bad state\u201d. There are specific examples both in the blog post above and also in this F# for Fun and Profit \u201c(https://fsharpforfunandprofit.com/posts/designing-with-types-making-illegal-states-unrepresentable/) Designing with types \u201d post that make it clear. The latter makes an excellent point why people do not often go all the way \u2014 it often requires flipping assumptions about a system, and also not wanting to bother defining a type (for example) for a container that can hold either one or three items: At this point, you might be saying that we have made things unnecessarily complicated.  People often see this upfront work as complicating the code (\u201cWhy can\u2019t I just validate in the initializer and be done?\u201d) and don\u2019t do it. Down the line, someone else doesn\u2019t know about the (unenforced) requirements, and that\u2019s when bugs are introduced. If this idea is implemented well, then the API design or the compiler will make it impossible to make a mistake, because you cannot use the code in a way that represents a bad state. It may be tedious but it\u2019s so worth it. 3. Errors as values This notion is well-described in \u201c(https://jessewarden.com/2021/04/errors-as-values.html) Errors as Values: Free Yourself From Unexpected Runtime Exceptions \u201d. The \u201cvalues\u201d in the phrase refers to returning an error value from a function instead of raising an exception. Usually, the error value is well-defined, and exceptions are unexpected. This idea is now fairly mainstream and I see that people try to avoid using exceptions. Many languages encourage and support you to return something like a Result (which, for example in Swift, is a sum type of Success and Error ) or, like Go, a tuple of values: a successful result and an optional error. This practice came to replace setting a global error variable or returning magic \u201cerror\u201d values (like in C) and throwing exceptions (like in C++, Java, etc.). To me, this simply makes more sense: isn\u2019t it objectively better to get a finite and predictable error value from a function than an unspecified exception that may or may not happen that you still have to guard against? 4. Functional core, imperative shell I was not familiar with this notion at all, but after reading (https://www.javiercasas.com/articles/functional-programming-patterns-functional-core-imperative-shell) this blog post by Javier Casas it now makes sense. Functional programming is, ideally, a realm of pure functions without side effects that are easy to reason about and easily testable in isolation. To integrate this code into real systems in the real world you need to set up an environment, procure and provide input values, maybe talk to the OS (which is often stateful) and communicate output values. That\u2019s where the dichotomy comes from. You push as much code as possible that deals with logic into a \u201cfunctional core\u201d, and only the messy interfacing routines end up in an \u201cimperative shell\u201d. This way the meat of the program can get easily tested without any scaffolding, and the test environment and possibly mocks only need to be provided to the shell. 5. Smart constructor The page \u201c(https://wiki.haskell.org/index.php?title=Smart_constructors) Smart constructors \u201d on the Haskell Wiki explains this notion well. It neatly dovetails with \u201cmaking illegal states unrepresentable\u201d by providing compile- or at least runtime checks when making new values. If the type system cannot enforce a constraint, you can prevent \u201cbad\u201d values from being constructed at runtime. The page raises another point, that smart constructors can do optimizations because they control internal representation. The example on the page is compacting data, but it can also be normalization, making structures homogenous or converting value formats. The benefit is, again, creating a solid foundation that you can build on. You know the data you operate is clean, tidy and valid. This means you can do fewer checks later in the pipeline which improves both performance (less code is executed) and clarity (there are fewer noisy checks). Conclusion Do these ideas belong only in functional programming? While they are practiced more there, and functional programming languages generally have strong type systems that help implement these constraints, we \u201cregular\u201d developers can use most of these concepts when writing our mostly imperative code to make it simpler. Clearly, if your functions have few or no side effects, the underlying data is clean, and you can\u2019t accidentally create \u201cbad\u201d objects, you\u2019ll have an easier time.  (/tags/programming/) Programming  Subscribe to NorikiTech Get infrequent updates by email: what's new on the blog and project news.  (Email address)    Subscribe  Loading...     (1) Subscribe  Loading...   (true)   Thank you! You have successfully joined our subscriber list.         \u00a9 Yuri Karabatov \u2234 (https://x.com/karabatov) X , (https://bsky.app/profile/yk.wtf) Bluesky , (https://dat-a.com/@ykar) Mastodon  Powered by (https://ddpub.org) DDPub \u00a7    "
                ],
                "output": "htmltotext.txt",
                "pwd": "/data/archive/1732743678.143301",
                "schema": "ArchiveResult",
                "start_ts": "2024-11-27T21:41:45.135195+00:00",
                "status": "succeeded"
            }
        ],
        "media": [
            {
                "cmd": [
                    "/usr/local/bin/yt-dlp",
                    "--restrict-filenames",
                    "--trim-filenames",
                    "128",
                    "--write-description",
                    "--write-info-json",
                    "--write-annotations",
                    "--write-thumbnail",
                    "--no-call-home",
                    "--write-sub",
                    "--write-auto-subs",
                    "--convert-subs=srt",
                    "--yes-playlist",
                    "--continue",
                    "--no-abort-on-error",
                    "--ignore-errors",
                    "--geo-bypass",
                    "--add-metadata",
                    "--format=(bv*+ba/b)[filesize<=750m][filesize_approx<=?750m]/(bv*+ba/b)",
                    "--skip-download",
                    "--cache-dir=/data/yt-dlp-cache/",
                    "--cookies=/data/yt-dlp-cache/cookies.txt",
                    "--proxy=socks5://tor-socks-proxy:9150",
                    "--no-playlist",
                    "https://norikitech.com/posts/functional-affirmations/"
                ],
                "cmd_version": "2024.10.7",
                "end_ts": "2024-11-27T21:41:52.761969+00:00",
                "index_texts": [],
                "output": "media/",
                "pwd": "/data/archive/1732743678.143301",
                "schema": "ArchiveResult",
                "start_ts": "2024-11-27T21:41:47.251029+00:00",
                "status": "succeeded"
            }
        ],
        "mercury": [
            {
                "cmd": [
                    "/home/archivebox/.npm/bin/postlight-parser",
                    "https://norikitech.com/posts/functional-affirmations/"
                ],
                "cmd_version": "2.2.3",
                "end_ts": "2024-11-27T21:41:45.103973+00:00",
                "index_texts": null,
                "output": "mercury/",
                "pwd": "/data/archive/1732743678.143301",
                "schema": "ArchiveResult",
                "start_ts": "2024-11-27T21:41:41.464431+00:00",
                "status": "succeeded"
            }
        ],
        "pdf": [],
        "readability": [
            {
                "cmd": [
                    "/home/archivebox/.npm/bin/readability-extractor",
                    "/tmp/tmprpl802kf",
                    "https://norikitech.com/posts/functional-affirmations/"
                ],
                "cmd_version": "0.0.11",
                "end_ts": "2024-11-27T21:41:33.635326+00:00",
                "index_texts": [
                    "Dmitrii Kovanikov writes posts about enterprise software development and functional programming, both entertaining and serious. I never used a functional programming language (such as Haskell or OCaml) at work, but several ideas from functional programming have become popular in mainstream general-purpose programming languages such as Swift (which I mostly write). Often even a partially functional approach produces simpler code that\u2019s easy to understand and quick to write and maintain. I\u2019m always looking for good ideas I can adopt to write better code, whatever their source.\n\nDmitrii recently posted a tweet (now also reposted to Bluesky) titled \u201cFunctional programming self-affirmations\u201d that listed five items:\n\n\nParse, don\u2019t validate\nMake illegal states unrepresentable\nErrors as values\nFunctional core, imperative shell\nSmart constructor\n\n\nThese got me interested, but as usual, the problem with such distilled mantras is that you need a lot of context to understand and use them. What does it mean to have a \u201csmart constructor\u201d? In the comments  Dmitrii himself and other people added links to suggested reading. Let\u2019s go over each item and uncover what it means.\n\n1. Parse, don\u2019t validate\n\nThe text that introduced this notion is this blog post with the same name written by Alexis King. It also covers the next notion so we\u2019ll come back to it later. It gives a much better explanation than my second-hand version could ever do.\n\nFor me the two standout ideas were:\n\n\nWhile validation establishes correctness, parsing enriches the data and adds knowledge we can build upon, and does that early on the way from less-structured to more-structured data. In my own work I describe it as \u201cbuilding a solid foundation\u201d meaning that you can confidently build higher-level abstractions when the lower-level abstractions are leak-proof, with correctness likely enforced by the compiler.\nThe notion of shotgun parsing (a mix of input validation and parsing that tries to capture all the \u201cbad\u201d cases \u2014 an antipattern, because it doesn\u2019t work) defined in the paper \u201cThe Seven Turrets of Babel: A Taxonomy of LangSec Errors and How to Expunge Them\u201d. It is exactly the same situation as writing tests: the tests cannot prove the absence of bugs, only that certain things are correct. To come closer to proven correctness, illegal states must be forced out of the program, usually with robust type definitions.\n\n\nWhich neatly leads us to the next point\u2026\n\n2. Make illegal states unrepresentable\n\nThis is an idea that I see talked about fairly often even in \u201cregular\u201d commercial programming. However, in my experience, not many people are willing to go all the way to achieve it. The post I linked above talks about this notion in its second half. I define it to mean \u201cyour logic and types (where applicable) are designed in such a way as to make it physically impossible to create a bad state\u201d.\n\nThere are specific examples both in the blog post above and also in this F# for Fun and Profit \u201cDesigning with types\u201d post that make it clear. The latter makes an excellent point why people do not often go all the way \u2014 it often requires flipping assumptions about a system, and also not wanting to bother defining a type (for example) for a container that can hold either one or three items:\n\n\nAt this point, you might be saying that we have made things unnecessarily complicated.\n\n\nPeople often see this upfront work as complicating the code (\u201cWhy can\u2019t I just validate in the initializer and be done?\u201d) and don\u2019t do it. Down the line, someone else doesn\u2019t know about the (unenforced) requirements, and that\u2019s when bugs are introduced. If this idea is implemented well, then the API design or the compiler will make it impossible to make a mistake, because you cannot use the code in a way that represents a bad state. It may be tedious but it\u2019s so worth it.\n\n3. Errors as values\n\nThis notion is well-described in \u201cErrors as Values: Free Yourself From Unexpected Runtime Exceptions\u201d. The \u201cvalues\u201d in the phrase refers to returning an error value from a function instead of raising an exception. Usually, the error value is well-defined, and exceptions are unexpected.\n\nThis idea is now fairly mainstream and I see that people try to avoid using exceptions. Many languages encourage and support you to return something like a Result (which, for example in Swift, is a sum type of Success and Error) or, like Go, a tuple of values: a successful result and an optional error. This practice came to replace setting a global error variable or returning magic \u201cerror\u201d values (like in C) and throwing exceptions (like in C++, Java, etc.).\n\nTo me, this simply makes more sense: isn\u2019t it objectively better to get a finite and predictable error value from a function than an unspecified exception that may or may not happen that you still have to guard against?\n\n4. Functional core, imperative shell\n\nI was not familiar with this notion at all, but after reading this blog post by Javier Casas it now makes sense. Functional programming is, ideally, a realm of pure functions without side effects that are easy to reason about and easily testable in isolation.\n\nTo integrate this code into real systems in the real world you need to set up an environment, procure and provide input values, maybe talk to the OS (which is often stateful) and communicate output values. That\u2019s where the dichotomy comes from. You push as much code as possible that deals with logic into a \u201cfunctional core\u201d, and only the messy interfacing routines end up in an \u201cimperative shell\u201d. This way the meat of the program can get easily tested without any scaffolding, and the test environment and possibly mocks only need to be provided to the shell.\n\n5. Smart constructor\n\nThe page \u201cSmart constructors\u201d on the Haskell Wiki explains this notion well. It neatly dovetails with \u201cmaking illegal states unrepresentable\u201d by providing compile- or at least runtime checks when making new values. If the type system cannot enforce a constraint, you can prevent \u201cbad\u201d values from being constructed at runtime.\n\nThe page raises another point, that smart constructors can do optimizations because they control internal representation. The example on the page is compacting data, but it can also be normalization, making structures homogenous or converting value formats.\n\nThe benefit is, again, creating a solid foundation that you can build on. You know the data you operate is clean, tidy and valid. This means you can do fewer checks later in the pipeline which improves both performance (less code is executed) and clarity (there are fewer noisy checks).\n\nConclusion\n\nDo these ideas belong only in functional programming? While they are practiced more there, and functional programming languages generally have strong type systems that help implement these constraints, we \u201cregular\u201d developers can use most of these concepts when writing our mostly imperative code to make it simpler. Clearly, if your functions have few or no side effects, the underlying data is clean, and you can\u2019t accidentally create \u201cbad\u201d objects, you\u2019ll have an easier time."
                ],
                "output": "readability/",
                "pwd": "/data/archive/1732743678.143301",
                "schema": "ArchiveResult",
                "start_ts": "2024-11-27T21:41:30.612314+00:00",
                "status": "succeeded"
            }
        ],
        "screenshot": [],
        "singlefile": [],
        "title": [
            {
                "cmd": [
                    "/usr/bin/curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--max-time",
                    "60",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 ArchiveBox/{VERSION} (+https://github.com/ArchiveBox/ArchiveBox/)",
                    "https://norikitech.com/posts/functional-affirmations/"
                ],
                "cmd_version": "8.10.1",
                "end_ts": "2024-11-27T21:41:29.716723+00:00",
                "index_texts": null,
                "output": "Functional programming self-affirmations - NorikiTech",
                "pwd": "/data/archive/1732743678.143301",
                "schema": "ArchiveResult",
                "start_ts": "2024-11-27T21:41:29.691474+00:00",
                "status": "succeeded"
            }
        ],
        "wget": []
    },
    "icons": null,
    "is_archived": true,
    "is_static": false,
    "latest": {
        "archive_org": "https://web.archive.org/web/20241127214207/https://norikitech.com/posts/functional-affirmations/",
        "dom": "output.html",
        "favicon": "favicon.ico",
        "git": null,
        "media": "media/",
        "pdf": null,
        "screenshot": null,
        "singlefile": null,
        "title": "Functional programming self-affirmations - NorikiTech",
        "warc": null,
        "wget": null
    },
    "link_dir": "/data/archive/1732743678.143301",
    "newest_archive_date": "2024-11-27T21:41:52.872875+00:00",
    "num_failures": 0,
    "num_outputs": 9,
    "oldest_archive_date": "2024-11-27T21:41:19.941842+00:00",
    "path": "/posts/functional-affirmations/",
    "schema": "Link",
    "scheme": "https",
    "snapshot_abid": "snp_01JDQS4J7657D220B601YMZ4YM",
    "snapshot_id": "debbcf7f-d0d1-415f-b6d8-a9c43d4f93d4",
    "sources": [
        "/data/sources/1732743676-import.txt"
    ],
    "tags": null,
    "tags_str": "",
    "timestamp": "1732743678.143301",
    "title": "Functional programming self-affirmations - NorikiTech",
    "url": "https://norikitech.com/posts/functional-affirmations/"
}