{"id":24551947,"url":"https://github.com/mlabs-haskell/styleguide","last_synced_at":"2025-10-19T23:22:28.728Z","repository":{"id":39900787,"uuid":"385732661","full_name":"mlabs-haskell/styleguide","owner":"mlabs-haskell","description":null,"archived":false,"fork":false,"pushed_at":"2024-11-02T15:13:53.000Z","size":31,"stargazers_count":16,"open_issues_count":10,"forks_count":6,"subscribers_count":11,"default_branch":"main","last_synced_at":"2025-03-29T02:04:19.673Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Nix","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mlabs-haskell.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-07-13T20:53:55.000Z","updated_at":"2025-03-10T14:47:02.000Z","dependencies_parsed_at":"2023-12-20T11:04:02.793Z","dependency_job_id":"3e5e933c-45c1-4ed8-8ea8-ef24b4cee05b","html_url":"https://github.com/mlabs-haskell/styleguide","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlabs-haskell%2Fstyleguide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlabs-haskell%2Fstyleguide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlabs-haskell%2Fstyleguide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlabs-haskell%2Fstyleguide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mlabs-haskell","download_url":"https://codeload.github.com/mlabs-haskell/styleguide/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249161924,"owners_count":21222570,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-23T01:19:30.657Z","updated_at":"2025-10-19T23:22:28.637Z","avatar_url":"https://github.com/mlabs-haskell.png","language":"Nix","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Use it!\n\nIn your flake, add\n\n```nix\n{ \n  inputs.styleguide.url = \"github:mlabs-haskell/style-guide\";\n\n  outputs = inputs @ {...}: inputs.flake-utils.lib.eachDefaultSystem (system: {\n    # ... or your preferred way to handle ${system}\n    checks.format = inputs.styleguide.lib.${system}.mkCheck self;\n    formatter = inputs.styleguide.lib.${system}.mkFormatter self;\n  });\n}\n```\n\nRun `nix fmt` to format your code. Build `checks.${system}.format` in CI to check formatting.\n\n# Introduction\n\nThis document describes a set of standards for code. It also explains our reasoning \nfor these choices, and acts as a living\ndocument of our practices for current and future contributors to the project. We\nintend for this document to evolve as our needs change, as well as act as a\nsingle point of truth for standards.\n\n# Motivation\n\nThe desired outcomes from the prescriptions in this document are as follows.\n\n## Increase consistency\n\nInconsistency is worse than _any_ standard, as it requires us to track a large\namount of case-specific information. Software development is already a difficult\ntask due to the inherent complexities of the problems we seek to solve, as well\nas the inherent complexities foisted upon us by _decades_ of bad historical\nchoices we have no control over. For newcomers to a project and old hands alike,\nincreased inconsistency translates to developmental friction, resulting in\nwasted time, frustration and ultimately, worse outcomes for the code in\nquestion.\n\nTo avoid putting ourselves into this boat, both currently and in the future, we\nmust strive to be _automatically consistent_. Similar things should look\nsimilar; different things should look different; as much as possible, we must\npick some rules _and stick to them_; and this has to be clear, explicit and\nwell-motivated. This will ultimately benefit us, in both the short and the long\nterm. The standards described here, as well as this document itself, is written\nwith this foremost in mind.\n\n## Limit non-local information\n\nThere is a limited amount of space in a developer's skull; we all have bad days,\nand we forget things or make decisions that, perhaps, may not be ideal at the\ntime. Therefore, limiting cognitive load is good for us, as it reduces the\namount of trouble we can inflict due to said skull limitations. One of the worst\ncontributors to cognitive load (after inconsistency) is _non-local information_\n- the requirement to have some understanding beyond the scope of the current\nunit of work. That unit of work can be a data type, a module, or even a whole\nproject; in all cases, the more non-local information we require ourselves to\nhold in our minds, the less space that leaves for actually doing the task at\nhand, and the more errors we will introduce as a consequence.\n\nThus, we must limit the need for non-local information at all possible levels.\n'Magic' of any sort must be avoided; as much locality as possible must be\npresent everywhere; needless duplication of effort or result must be avoided.\nThus, our work must be broken down into discrete, minimal, logical units, which\ncan be analyzed, worked on, reviewed and tested in as much isolation as\npossible. This also applies to our external dependencies.\n\nThus, many of the decisions described here are oriented around limiting the\namount of non-local knowledge required at all levels of the codebase.\nAdditionally, we aim to avoid doing things 'just because we can' in a way that\nwould be difficult for other Haskellers to follow, regardless of skill level.\n\n## Minimize impact of legacy\n\nHaskell is a language that is older than some of the people currently writing\nit; parts of its ecosystem are not exempt from it. With age comes legacy, and\nmuch of it is based on historical decisions which we now know to be problematic\nor wrong. We can't avoid our history, but we can minimize its impact on our\ncurrent work. \n\nThus, we aim to codify good practices in this document _as seen today_. We also\ntry to avoid obvious 'sharp edges' by proscribing them away in a principled,\nconsistent and justifiable manner. \n\n## Automate away drudgery\n\nAs developers, we should use our tools to make ourselves as productive as\npossible. There is no reason for us to do a task if a machine could do it for\nus, especially when this task is something boring or repetitive. We love Haskell\nas a language not least of all for its capability to abstract, to describe, and\nto make fun what other languages make dull or impossible; likewise, our work\nmust do the same.\n\nMany of the tool-related proscriptions and requirements in this document are\ndriven by a desire to remove boring, repetitive tasks that don't need a human to\nperform. By removing the need for us to think about such things, we can focus on\nthose things which _do_ need a human; thus, we get more done, quicker.\n\n# Conventions\n\nThe words MUST, SHOULD, MUST NOT, SHOULD NOT and MAY are defined as per [RFC\n2119][rfc-2119].\n\n# Tools\n\n## Compiler warning settings\n\nThe following warnings MUST be enabled for all builds of any project, or any\nproject component, in the `ghc-options` of the Cabal file:\n\n* ``-Wall``\n* ``-Wcompat``\n* ``-Wincomplete-uni-patterns``\n* ``-Wincomplete-record-updates``\n* ``-Wredundant-constraints``\n* ``-Wmissing-export-lists``\n* ``-Wmissing-deriving-strategies``\n* ``-Werror``\n\nAdditionally, ``-Wredundant-constraints`` SHOULD be enabled for all builds of\nany project, in the `ghc-options` of the Cabal file. Exceptions are allowed \nwhen the additional constraints are designed to ensure safety, rather than due \nto reliance on any method. If this warning is to be disabled, it MUST be\ndisabled in the narrowest possible scope; ideally, this SHOULD be a single\nmodule.\n\n### Justification\n\nMost of these options are suggested by [Alexis King][alexis-king-options] - the\njustifications for them can be found at the link. These fit well with our\nmotivations, and thus, should be used everywhere. The ``-Werror`` ensures that\nwarnings _cannot_ be ignored: this means that problems get fixed sooner. We also\nadd ``-Wmissing-export-lists`` and ``-Wmissing-deriving-strategies``: the first\nensures that we clearly indicate what is, and isn't, part of a module's public\nAPI, and the second ensures that we have clarity about how everything is\nderived. As we mandate both export lists and deriving strategies in this\ndocument, these warnings ensure compliance, as well as checking it\nautomatically.\n\nThe permissible exception stems from how redundant constraints are detected by\nGHC; basically, unless a type class method from a constraint is used within the\nbody of a definition, that constraint is deemed redundant. This is mostly\naccurate, but some type-level safety constraints can be deemed redundant as a\nresult of this approach. In this case, a limited disabling (per module, ideally)\nof ``-Wredundant-constraints`` is acceptable, as it represents a workaround to\na technical problem, not an issue with the warning itself.\n\n## Linting\n\nEvery source file MUST be free of warnings as produced by [HLint][hlint], using\nthe settings described in `.hlint.yaml`. A copy of such a file is provided in\nthis repository.\n\n### Justification\n\nHLint automates away the detection of many common sources of boilerplate and\ninefficiency. It also describes many useful refactors, which in many cases make\nthe code easier to read and understand. As this is fully automatic, it saves\neffort on our part, and ensures consistency across the codebase without us\nhaving to think about it.\n\n## Code formatting\n\nEvery source file MUST be formatted according to [Fourmolu][fourmolu], with the\nfollowing settings (as per its settings file):\n\n* ``indentation: 2``\n* ``comma-style: leading``\n* ``record-brace-space: true``\n* ``indent-wheres: true``\n* ``diff-friendly-import-export: true``\n* ``respectful: true``\n* ``haddock-style: multi-line``\n* ``newlines-between-decls: 1``\n\nA copy of a configuration file with these settings is provided in this\nrepository.\n\nEach source code line MUST be at most 80 characters wide.\n\n### Justification\n\nConsistency is the most important goal of readable codebases. Having a single\nstandard, automatically enforced, means that we can be sure that everything will\nlook similar, and not have to spend time or mind-space ensuring that our code\ncomplies. It also helps with `git diff`s, as it 'spreads around' the differences\nless.\n\nLines wider than 80 characters become difficult to read, especially when viewed\non a split screen. It is also a long-standing convention, not just in Haskell.\nLastly, very long lines tend to indicate that we need better naming or\nrefactoring.\n\n# Code practices\n\n## Naming\n\ncamelCase MUST be used for all non-type, non-data-constructor names; otherwise,\nTitleCase MUST be used. Acronyms used as part of a naming identifier (such as \n'JSON', 'API', etc) SHOULD be downcased; thus ``repairJson`` and\n``fromHttpService`` are correct. Exceptions are allowed for external libraries\n(Aeson's ``parseJSON`` for example).\n\n### Justification\n\ncamelCase for non-type, non-data-constructor names is a long-standing convention\nin Haskell (in fact, HLint checks for it); TitleCase for type names or data\nconstructors is _mandatory_. Obeying such conventions reduces cognitive load, as\nit is common practice among the entire Haskell ecosystem. There is no particular\nstandard regarding acronym casing: examples of always upcasing exist (Aeson) as\nwell as examples of downcasing (``http-api-data``). One choice for consistency\n(or as much as is possible) should be made however.\n\n## Modules\n\n### Imports\n\nAll modules MUST use the following conventions for imports:\n\n* ``import Foo (Baz (Quux, quux), Bar, frob)``\n* ``import qualified Bar.Foo as Foo``\n\nIf `ImportQualifiedPost` is enabled, the following form MAY also be used:\n\n* ``import Bar.Foo qualified as Foo``\n\nSome specific examples cases follow. Type class methods SHOULD be imported\nalongside their class:\n\n```haskell\nimport Control.Applicative (Alternative ((\u003c|\u003e)))\n```\n\nAn exception is given when only the method is required:\n\n```haskell\nimport Control.Applicative (empty)\n```\n\nRecord fields MUST be imported alongside their record:\n\n```haskell\nimport Data.Monoid (Endo (appEndo))\n```\n\nData types from modules imported qualified SHOULD be imported unqualified by\nthemselves:\n\n```\nimport Data.Vector (Vector)\nimport qualified Data.Vector as Vector\n```\n\nAn exception is given if such an import would cause a name clash:\n\n```haskell\n-- no way to import both of these without clashing on the Vector type name\nimport qualified Data.Vector as Basic\nimport qualified Data.Vector.Storable as Storable\n\n-- We now use Basic.Vector to refer to the Vector in Data.Vector, and\n-- Storable.Vector otherwise.\n\nWe also permit an exception to use a 'hiding import' to replace part of the\n``Prelude``:\n\n```haskell\n-- replace the String-based readFile with a Text-based one\nimport Prelude hiding (readFile)\nimport Data.Text.IO (readFile)\n```\n\nData constructors MUST be imported individually. For example, given the\nfollowing data type declaration:\n\n```haskell\nmodule Quux where\n\ndata Foo = Bar Int | Baz\n```\n\nIts corresponding import should be:\n\n```haskell\nimport Quux (Foo, Bar, Baz)\n```\n\nQualified imports SHOULD use their entire module name (that is, the last\ncomponent of its hierarchical name) as the prefix. For example:\n\n```haskell\nimport qualified Data.Vector as Vector\n```\n\nExceptions are granted when:\n\n* The import would cause a name clash anyway (such as different ``vector``\n  modules); or\n* We have to import a data type qualified as well.\n\nQualified imports of multiple modules MUST NOT be imported under the same name.\nThus, the following is wrong:\n\n```haskell\n-- Do not do this!\nimport qualified Foo.Bar as Baz\nimport qualified Foo.Quux as Baz\n```\n\n### Justification\n\nOne of the biggest challenges for modules which depend on other modules\n(especially ones that come from the project, rather than an external library) is\nknowing where a given identifier's definition can be found. Having explicit\nimports of the form described helps make this search as straightforward as\npossible. This also limits cognitive load when examining the sources (if we\ndon't import something, we don't need to care about it in general). Lastly,\nbeing explicit avoids stealing too many useful names.\n\nIn general, type names occur far more often in code than function calls: we have\nto use a type name every time we write a type signature, but it's unlikely we\nuse only one function that operates on said type. Thus, we want to reduce the\namount of extra noise needed to write a type name if possible. Additionally,\nname clashes from function names are far more likely than name clashes from type\nnames: consider the number of types on which a ``size`` function makes sense.\nThus, importing type names unqualified, even if the rest of the module is\nqualified, is good practice, and saves on a lot of prefixing.\n\n### Exports\n\nAll modules MUST have explicit export lists; that is, every module must state\nwhat exactly it exports. Export lists SHOULD be separated using Haddock\nheadings:\n\n```haskell\nmodule Foo.Bar (\n  -- * Types\n  Baz,\n  Quux (Quux),\n  -- * Construction\n  mkBaz,\n  quuxFromBaz,\n  -- etc\n  ) where\n```\n\nAn exception is granted when the module provides few exported identifiers, or if\nthe module doesn't have a large variety of functionality. In the specific case\nof modules that exist _only_ to provide instances (for compatibility, for\nexample), the export list MUST be empty.\n\nExports of data constructors or fields SHOULD be explicit:\n\n```haskell\n-- This is ideal\nmodule Foo.Bar (\n  Baz(Baz, quux, frob)\n  ) where\n```\n\nAn exception is granted if the number of fields or constructors is large; then,\nwildcard exports MAY be used:\n\n```haskell\n-- This is fine if Baz has a lot of constructors or fields\nmodule Foo.Bar (\n  Baz(..)\n  ) where\n```\n\n### Justification\n\nExplicit export lists are an immediate, clear and obvious indication of what\npublically visible interface a module provides. It gives us stability guarantees\n(namely, we know we can change things that aren't exported and not break\ndownstream code at compile time), and tells us where to go looking first when\ninspecting or learning the module. Additionally, it means there is less chance\nthat implementation details 'leak' out of the module due to errors on the part\nof developers, especially new developers.\n\nAllowing wildcard exports, while disallowing wildcard _imports_, is justified on\nthe grounds of information locality. Seeing a wildcard import of all of a type's\ndata constructors or fields doesn't necessarily indicate the usages of said data\nconstructors or fields without looking up the module from where they're\nexported; having this import be explicit reduces how much searching we have to\ndo. However, if we are reading an export list, we have the type definition in\nthe same file we're already looking at, making it fairly easy to check.\n\n## Plutus module import naming conventions\n\nIn addition to the general module import rules, we follow some conventions \non how we import the Plutus API modules, allowing for some flexibility \ndepending on the needs of a particular module.\n\nModules under the names `Plutus`, `Ledger` and `Plutus.V1.Ledger` SHOULD \nbe imported qualified with their module name, as per the general module standards. \nAn exception to this is `Plutus.V1.Ledger.Api`, where the `Ledger` name is preferred.\n\nSome other exceptions to this are allowed where it may be more convenient to \navoid longer qualified names.\n\nFor example:\n\n```haskell\nimport Plutus.V1.Ledger.Slot qualified as Slot\nimport Plutus.V1.Ledger.Tx qualified as Tx\nimport Plutus.V1.Ledger.Api qualified as Ledger\nimport Ledger.Oracle qualified as Oracle\nimport Plutus.Contract qualified as Contract\n```\n\nIn some cases it may be justified to use a shortened module name:\n\n```haskell\nimport Plutus.V1.Ledger.AddressMap qualified as AddrMap\n```\n\nModules under `PlutusTx` that are extensions to `PlutusTx.Prelude` MAY be \nimported unqualified when it is reasonable to do so. \n\nThe `Plutus.V1.Ledger.Api` module SHOULD be avoided in favour of more \nspecific modules where possible. For example, we should avoid:\n\n```haskell\nimport Plutus.V1.Ledger.Api qualified as Ledger\n```\n\nIn favour of:\n\n```haskell\nimport Plutus.V1.Ledger.Scripts qualified as Scripts\n```\n\n### Justification\n\nThe Plutus API modules can be confusing, with numerous modules involved, many \nexporting the same items. Consistent qualified names help ease this problem, \nand decrease ambiguity about where imported items come from.\n\n## LANGUAGE pragmata\n\nThe following pragmata MUST be enabled at project level (that is, in\nthe Cabal file):\n\n* ``BangPatterns``\n* ``BinaryLiterals``\n* ``ConstraintKinds``\n* ``DataKinds``\n* ``DeriveFunctor``\n* ``DeriveGeneric``\n* ``DeriveTraversable``\n* ``DerivingStrategies``\n* ``DerivingVia``\n* ``DuplicateRecordFields``\n* ``EmptyCase``\n* ``FlexibleContexts``\n* ``FlexibleInstances``\n* ``GADTs``\n* ``GeneralizedNewtypeDeriving``\n* ``HexFloatLiterals``\n* ``InstanceSigs``\n* ``ImportQualifiedPost``\n* ``KindSignatures``\n* ``LambdaCase``\n* ``MultiParamTypeClasses``\n* ``NoImplicitPrelude``\n* ``NumericUnderscores``\n* ``OverloadedStrings``\n* ``ScopedTypeVariables``\n* ``StandaloneDeriving``\n* ``TupleSections``\n* ``TypeApplications``\n* ``TypeOperators``\n* ``TypeSynonymInstances``\n* ``UndecidableInstances``\n\nAny other LANGUAGE pragmata MUST be enabled per-file. All language pragmata MUST\nbe at the top of the source file, written as ``{-# LANGUAGE PragmaName #-}``.\n\nFurthermore, the following pragmata MUST NOT be used, or enabled, anywhere:\n\n* ``DeriveDataTypeable``\n* ``DeriveFoldable``\n* ``PartialTypeSignatures``\n* ``PostfixOperators``\n\n### Justification\n\n``DataKinds``, ``DuplicateRecordFields``, ``GADTs``, ``TypeApplications``,\n``TypeSynonymInstances`` and ``UndecidableInstances`` are needed globally to use\nthe GHC plugin from ``record-dot-preprocessor``. While some of these extensions\nare undesirable to use globally, we end up needing them anyway, so we can't\nreally avoid this.\n\n``BangPatterns`` are a much more convenient way to force evaluation than\nrepeatedly using `seq`. Furthemore, they're not confusing, and are considered\nubiquitous enough for ``GHC2021``. Having them on by default simplifies a lot of\nperformance tuning work, and they don't really need signposting.\n\n``BinaryLiterals``, ``HexFloatLiterals`` and ``NumericUnderscores`` all simulate\nfeatures that are found in many other programming languages, and that are\nextremely convenient in a range of settings, ranging from dealing with large\nnumbers to bit-twiddling. If anything, it is more surprising and annoying when\nthese _aren't_ enabled, and should really be part of Haskell syntax anyway.\nEnabling this project-wide actually encourages better practice and readability.\n\nThe kind ``Constraint`` is not in Haskell2010, and thus, isn't recognized by\ndefault. While working with constraints as first-class objects isn't needed\noften, this extension effectively exists because Haskell2010 lacks exotic kinds\naltogether. Since we require explicit kind signatures (and foralls) for all type\nvariables, this needs to be enabled as well. There is no harm in enabling this\nglobally, as other rich kinds (such as ``Symbol`` or ``Nat``) don't require an\nextension for their use, and this doesn't change any behaviour (``Constraint``\nexists whether you enable this extension or not, as do 'exotic kinds' in\ngeneral).\n\n``DerivingStrategies`` is good practice (and in fact, is mandated by this\ndocument); it avoids ambiguities between ``GeneralizedNewtypeDeriving`` and\n``DeriveAnyClass``, allows considerable boilerplate savings through use of\n``DerivingVia``, and makes the intention of the derivation clear on immediate\nreading, reducing the amount of non-local information about derivation\npriorities that we have to retain. ``DeriveFunctor`` and\n``GeneralizedNewtypeDeriving`` are both obvious and useful extensions to the\nauto-derivation systems available in GHC. Both of these have only one correct\nderivation (the former given by [parametricity\nguarantees][functor-parametricity], the latter by the fact that a newtype only\nwraps a single value). As there is no chance of unexpected behaviour by these,\nno possible behaviour variation, and that they're key to supporting both the\n``stock`` and ``newtype`` deriving strategies, having these on by default removes\nconsiderable tedium and line noise from our code. A good example are newtype\nwrappers around monadic stacks:\n\n```haskell\nnewtype FooM a = FooM (ReaderT Int (StateT Text IO) a)\n  deriving newtype (\n    Functor, \n    Applicative, \n    Monad, \n    MonadReader Int, \n    MonadState Text, \n    MonadIO\n    )\n```\n\nDeriving ``Traversable`` is a little tricky. While ``Traversable`` is lawful\n(though not to the degree ``Functor`` is, permitting multiple implementations in\nmany cases), deriving it is complicated by issues of role assignation for\nhigher-kinded type variables and the fact that you can't ``coerce`` through a\n``Functor``. These are arguably implementation issues, but repairing this\nsituation requires cardinal changes to ``Functor``, which is unlikely to ever\nhappen. Even newtype or via derivations of ``Traversable`` are mostly\nimpossible; thus, we must have special support from GHC, which\n``DeriveTraversable`` enables. This is a very historically-motivated\ninconsistency, and should really not exist at all. While this only papers over\nthe problem (as even with this extension on, only stock derivations become\npossible), it at least means that it can be done at all. Having it enabled\nglobally makes this inconsistency slightly less visible, and is completely safe.\n\nWhile GHC ``Generic``s are far from problem-free, many parts of the Haskell\necosystem require ``Generic``, either as such (c.f. ``beam-core``) or for\nconvenience (c.f ``aeson``, ``hashable``). Additionally, several core parts of\nPlutus (including ``ToSchema``) are driven by ``Generic``. The derivation is\ntrivial in most cases, and having to enable an extension for it is quite\nannoying. Since no direct harm is done by doing this, and use of ``Generic`` is\nalready signposted clearly (and is mostly invisible), having this on globally\nposes no problems.\n\n``EmptyCase`` not being on by default is an inconsistency of Haskell 2010, as\nthe report allows us to define an empty data type, but without this extension,\nwe cannot exhaustively pattern match on it. This should be the default behaviour\nfor reasons of symmetry.\n\n``FlexibleContexts`` and ``FlexibleInstances`` paper over a major deficiency of\nHaskell2010, which in general isn't well-motivated. There is no real reason to\nrestrict type arguments to variables in either type class instances or type\nsignatures: the reasons for this choice in Haskell2010 are entirely for the\nconvenience of the implementation. It produces no ambiguities, and in many ways,\nthe fact this _isn't_ the default is more surprising than anything.\nAdditionally, many core libraries rely on one, or both, of these extensions\nbeing enabled (``mtl`` is the most obvious example, but there are many others).\nThus, even for popularity and compatibility reasons, these should be on by\ndefault.\n\n``InstanceSigs`` are harmless by default, and introduce no complications. Their\nnot being default is strange. ``ImportQualifiedPost`` is already a convention \nof several MLabs projects, and helps with formatting of imports. \n\n``KindSignatures`` become extremely useful in any setting where 'exotic kinds'\n(meaning, anything which isn't `Type` or `Type -\u003e Type` or similar) are\ncommonplace; much like type signatures clarify expectations and serve as active\ndocumentation (even where GHC can infer them), explicit kind signatures serve\nthe same purpose 'one level up'. When combined with the requirement to provide\nexplicit foralls for type variables defined in this document, they simplify the\nusage of 'exotic kinds' and provide additional help from both the type checker\nand the code. Since this project is Plutus-based, we use 'exotic kinds'\nextensively, especially in row-polymorphic records; thus, in our case, this is\nespecially important. This also serves as justification for\n`ScopedTypeVariables`, as well as ironing out a weird behaviour where in cases\nsuch as \n\n```haskell\nfoo :: a -\u003e b\nfoo = bar . baz\n   where\n      bar :: String -\u003e b\n      bar = ...\n      baz :: a -\u003e String\n      baz = ...\n```\n\ncause GHC to produce _fresh_ type variables in each ``where``-bind. This is\nconfusing and makes little sense - if the user wanted a fresh variable, they\nwould name it that way. What's worse is that the type checker emits an error\nthat makes little sense (except to those who have learned to look for this\nerror), creating even more confusion, especially in cases where the type\nvariable is constrained:\n\n```haskell\nfoo :: (Monoid m) =\u003e m -\u003e String\nfoo = bar . baz\n   where\n      baz :: m -\u003e Int\n      baz = ... -- this has no idea that m is a Monoid, since m is fresh!\n```\n\n``LambdaCase`` reduces a lot of code in the common case of analysis of sum\ntypes. Without it, we are forced to either write a dummy ``case`` argument:\n\n```haskell\nfoo s = case s of\n-- rest of code here\n```\n\nOr alternatively, we need multiple heads:\n\n```haskell\nfoo Bar = -- rest of code\nfoo (Baz x y) = -- rest of code\n-- etc\n```\n\n``LambdaCase`` is shorter than both of these, and avoids us having to bind\nvariables, only to pattern match them away immediately. It is convenient, clear\nfrom context, and really should be part of the language to begin with.\n\n``MultiParamTypeClasses`` are required for a large number of standard Haskell\nlibraries, including ``mtl`` and ``vector``, and in many situations. Almost any\nproject of non-trivial size must have this extension enabled somewhere, and if\nthe code makes significant use of ``mtl``-style monad transformers or defines\nanything non-trivial for ``vector``, it must use it. Additionally, it arguably\nlifts a purely implementation-driven decision of the Haskell 2010 language, much\nlike ``FlexibleContexts`` and ``FlexibleInstances``. Lastly, although it can\nintroduce ambiguity into type checking, it only applies when we want to define\nour own multi-parameter type classes, which is rarely necessary. Enabling it\nglobally is thus safe and convenient.\n\nBased on the recommendations of this document (driven by the needs of being \ncardinally connected with Plutus), ``NoImplicitPrelude`` is required to allow us \nto default to the Plutus prelude instead of the one from ``base``.\n\n``OverloadedStrings`` deals with the problem that ``String`` is a suboptimal\nchoice of string representation for basically _any_ problem, with the general\nrecommendation being to use ``Text`` instead. It is not, however, without its\nproblems: \n\n* ``ByteString``s are treated as ASCII strings by their ``IsString`` instance;\n* The semantics of Plutus' ``BuiltinByteString`` vary considerably by use site,\n  with little indication;\n* Overly polymorphic behaviour of many functions (especially in the presence of\n  type classes) forces extra type signatures;\n\nThese are usually caused not by the extension itself, but by other libraries and\ntheir implementations of either ``IsString`` or overly polymorphic use of type\nclasses without appropriate laws (Aeson's ``KeyValue`` is a particularly\negregious offender here). The convenience of this extension in the presence of\nliterals, and the fact that for ``BuiltinByteString`` there _is_ no other way to\nconstruct literals, makes it worth using by default.\n\n``StandaloneDeriving`` is mostly needed for GADTs, or situations where complex\ntype-level computations drive type class instances, requiring users to specify\nconstraints manually. This can pose some difficulties syntactically (such as\nwith deriving strategies), but isn't a problem in and of itself, as it doesn't\nreally change how the language works. Having this enabled globally is not\nproblematic.\n\n``TupleSections`` smooths out an oddity in the syntax of Haskell 2010 regarding\npartial application of tuple constructors. Given a function like ``foo :: Int -\u003e String -\u003e\nBar``, we accept it as natural that we can write ``foo 10`` to get a function of\ntype ``String -\u003e Bar``. However, by default, this logic doesn't apply to tuple\nconstructors. As special cases are annoying to keep track of, and in this case,\nserve no purpose, as well as being clear from their consistent use, this should\nalso be enabled by default; it's not clear why it isn't already.\n\n``TypeOperators`` is practically a necessity when dealing with type-level\nprogramming seriously. Much how infix data constructors are extremely useful\n(and sometimes clearer than their prefix forms), infix _type_ constructors serve\na similar function. Additionally, Plutus relies on operators at the type\nlevel significantly - for example, it's not really possible to define a\nrow-polymorphic record or variant without them. Having to enable this almost\neverywhere is a needless chore, and having type constructors behaving\ndifferently to data constructors here is a needless source of inconsistency.\n\nWe exclude ``DeriveDataTypeable``, as ``Data`` is a strictly-worse legacy\nversion of ``Generic``, and ``Typeable`` no longer needs deriving for anything\nanyway. The only reason to derive either of these is for compatibility with\nlegacy libraries, which we don't have any of, and the number of which shrinks\nevery year. If we're using this extension at all, it's probably a mistake.\n\n``Foldable`` is possibly the most widely-used lawless type class. Its only laws\nare about self-consistency (such as agreement between ``foldMap`` and\n``foldr``), but unlike something like ``Functor``, ``Foldable`` doesn't have any \nlaws specifying its behaviour, outside of consistency laws (such as between\n`foldMap` and `foldr`) and 'it compiles'. As a result, even if we\naccept its usefulness (a debatable position in itself), there are large numbers\nof possible implementations that could be deemed 'valid'. The approach taken by\n``DeriveFoldable`` is _one_ such approach, but this requires knowing its\nderivation algorithm, and may well not be the implementation you need. Unlike a\n``Functor`` derivation (whose meaning is obvious), a ``Foldable`` one is\nanything but, and requires referencing a lot of non-local information to\ndetermine how it will behave (especially for the 'richer' ``Foldable``, with\nmany additional methods). If you need a ``Foldable`` instance, you will either\nnewtype or via-derive it (which doesn't need this extension anyway), or you'll\nwrite your own (which _also_ doesn't need this extension). Enabling this\nencourages bad practices, is confusing, and ultimately doesn't really benefit\nanything.\n\n``PartialTypeSignatures`` is a misfeature. Allowing leaving in type holes (to be\nfilled by GHC's inference algorithm) is an anti-pattern for the same reason that\nnot providing top-level signatures is: while it's possible (mostly) for GHC to\ninfer signatures, we lose considerable clarity and active documentation by doing\nso, in return for (quite minor) convenience. While the use of typed holes during\ndevelopment is a good practice, they should not remain in final code. Given that\nPlutus projects require us to do some fairly advanced type-level programming\n(where inference often _fails_), this extension can often provide totally\nincorrect results due to GHC's 'best-effort' attempts at type checking. There is\nno reason to leave behind typed holes instead of filling them in, and we\nshouldn't encourage this.\n\n``PostfixOperators`` are arguably a misfeature. Infix operators already require\na range of special cases to support properly (what symbols create an infix\noperator, importing them at the value and type level, etc), which postfix\noperators make _worse_. Furthermore, they are seldom, if ever, used, and\ntypically aren't worth the trouble. Haskell is not Forth, none of our\ndependencies rely on postfix operators, and defining our own creates more\nproblems than it solves.\n\n## ``record-dot-preprocessor``\n\nThe GHC plugin from ``record-dot-preprocessor`` SHOULD be enabled globally. \n\n### Justification\n\nHaskell records are documentedly and justifiably subpar: the [original issue for\nthe record dot preprocessor extension][rdp-issue] provides a good summary of the\nreasons. While a range of extensions (including ``DuplicateRecordFields``,\n``DisambiguateRecordFields``, ``NamedFieldPuns``, and many others) have been\nproposed, and accepted, to mitigate the situation, the reality is that, even\nwith them in place, use of records in Haskell is considerably more difficult,\nand less flexible, than in any other language in widespread use today. The\nproposal described in the previous link provides a solution which is familiar to\nusers of most other languages, and addresses the fundamental issue that makes\nHaskell records so awkward.\n\nWhile the proposal for the record dot syntax that this preprocessor enables is\ncoming, it's not available in the current version of Haskell used by Plutus (and\nthus, transitively, by us). Additionally, the earliest this will be available is\nGHC 9.2, and given that our dependencies must support this version too, it'll be\nconsiderable time before we can get its benefits. The preprocessor gives us\nthese benefits immediately, at some dependency cost. While it's not a perfect\nprocess, as it involves enabling several questionable extensions, and can\nrequire disabling an important warning, it significantly reduces issues with\nrecord use, making it worthwhile. Additionally, when GHC 9.2 becomes usable, we\ncan upgrade to it seamlessly.\n\n## Prelude\n\nThe ``PlutusTx.Prelude`` MUST be used. A 'hiding import' to remove functionality\nwe want to replace SHOULD be used when necessary. If functionality from the\n``Prelude`` in ``base`` is needed, it SHOULD be imported qualified. Other\npreludes MUST NOT be used.\n\n### Justification\n\nFor Plutus, we are in some ways limited by what\nPlutus requires (and provides). Especially for on-chain code, the Plutus prelude\nis the one we need to use, and therefore, its use should be as friction-free as\npossible. As many modules may contain a mix of off-chain and on-chain code, we\nalso want to make impendance mismatches as limited as possible.\n\nWe can assume a familiarity (or at least,\nthe goal of such) with Plutus stuff. Additionally, _every_ Haskell developer is\nfamiliar with the ``Prelude`` from ``base``. Thus, any replacements of the\nPlutus prelude functionality with the ``base`` prelude should be clearly\nindicated locally.\n\nHaskell is a 30-year-old language, and the ``Prelude`` is one of its biggest\nsources of legacy. A lot of its defaults are questionable at best, and often\nneed replacing. As a consequence of this, a range of 'better ``Prelude``s' have\nbeen written, with a range of opinions: while there is a common core, a large\nnumber of decisions are opinionated in ways more appropriate to the authors of\nsaid alternatives and their needs than those of other users of said\nalternatives. This means that, when a non-``base`` ``Prelude`` is in scope, it\noften requires familiarity with its specific decisions, in addition to whatever\ncognitive load the current module and its other imports impose. Given that we\nalready use an alternative prelude (in tandem with the one from ``base``),\nadditional alternatives present an unnecessary cognitive load. Lastly, the \ndependency footprint of many alternative ``Prelude``s is _highly_ non-trivial; \nit isn't clear if we need all of this in our dependency tree.\n\nFor all of the above reasons, the best choice is 'default to Plutus, with local\nreplacements from `base`'.\n\n## Versioning\n\nA project MUST use the [PVP][pvp]. Two, and only two, version numbers MUST be\nused: a major version and a minor version.\n\n### Justification\n\nThe [Package Versioning Policy][pvp] is the conventional Haskell versioning\nscheme, adopted by most packages on Hackage. It is clearly described, and even\nautomatically verifiable by use of tools like [``policeman``][policeman]. Thus,\nadopting it is both in line with community standards (making it easier to\nremember), and simplifies cases such as Hackage publication or open-sourcing in\ngeneral.\n\nTwo version numbers (major and minor) is the minimum allowed by the PVP,\nindicating compilation-breaking and compilation-non-breaking changes\nrespectively. As parsimony is best, and more granularity than this isn't\ngenerally necessary, adopting this model is the right decision.\n\n## Documentation\n\nEvery publically-exported definition MUST have a Haddock comment, detailing its\npurpose. If a definition is a function, it SHOULD also have examples of use\nusing [Bird tracks][bird-tracks]. The Haddock for a publically-exported\ndefinition SHOULD also provide an explanation of any caveats, complexities of\nits use, or common issues a user is likely to encounter. \n\nIf the code project is a library, these Haddock comments SHOULD carry an\n[``@since``][haddock-since] annotation, stating what version of the library they\nwere introduced in, or the last version where their functionality or type\nsignature changed.\n\nFor type classes, their laws MUST be documented using a Haddock comment.\n\nEach repository must also have a README which should explain how to build the application\nand/or library. If the repository contains one or more executable, the readme should also \nexplain how to run each executable, including command line arguments/options.\n\n### Justification\n\nCode reading is a difficult task, especially when the 'why' rather than the\n'how' of the code needs to be deduced. A good solution to this is documentation,\nespecially when this documentation specifies common issues, provides examples of\nuse, and generally states the rationale behind the definition.\n\nFor libraries, it is often important to inform users what changed in a given\nversion, especially where 'major bumps' are concerned. While this would ideally\nbe addressed with accurate changelogging, it can be difficult to give proper\ncontext. ``@since`` annotations provide a granular means to indicate the last\ntime a definition changed considerably, allowing someone to quickly determine\nwhether a version change affects something they are concerned with.\n\nAs stated elsewhere in the document, type classes having laws is critical to our\nability to use equational reasoning, as well as a clear indication of what\ninstances are and aren't permissible. These laws need to be clearly stated, as\nthis assists both those seeking to understand the purpose of the type class, and\nalso the expected behaviour of its instances.\n\n## Type and kind signatures\n\nAll module-level definitions, as well as ``where``-binds, MUST have explicit type\nsignatures. Type variables MUST have an explicit ``forall`` scoping them, and\nall type variables MUST have explicit kind signatures. Thus, the following is\nwrong:\n\n```haskell\ndata Foo a = Bar | Baz [a]\n\nquux :: (Monoid m) =\u003e [m] -\u003e m -\u003e m\n```\n\nInstead, write it like this:\n\n```haskell\ndata Foo (a :: Type) = Bar | Baz [a]\n\nquux :: forall (m :: Type) . (Monoid m) =\u003e [m] -\u003e m -\u003e m\n```\n\nEach explicit type signature MUST correspond to one definition only. Thus, the\nfollowing is wrong:\n\n```haskell\nbar :: Int\nbaz :: Int\n(bar, baz) = someOtherFunction someOtherValue\n```\n\nInstead, write it like this:\n\n```haskell\nbar :: Int\nbar = fst . someOtherFunction $ someOtherValue\n\nbaz :: Int\nbaz = snd . someOtherFunction $ someOtherValue\n```\n\n### Justification\n\nExplicit type signatures for module-level definitions are a good practice in\nHaskell for several reasons: they aid type-driven development by providing\nbetter compiler feedback, act as a form of 'active documentation' describing\nwhat we expect a function to do (and _not_ do), and help us plan and formulate\nour thoughts while we implement. While GHC can, in theory, infer type\nsignatures, not having them significantly impedes readability, and can easily go\nwrong in the presence of more advanced type-level features (or even rank-2\npolymorphism, which is ubiquitous in the form of the ``ST`` monad at least);\nthere is no reason not to have them.\n\nType-level programming is mandated in many places by Plutus (including, but not\nlimited to, row-polymorphic records and variants from `Data.Row`). This often\nrequires use of ``TypeApplications``, which essentially makes not only the type\nvariables, but their _order_, part of the API of any definition that uses them.\nWhile there is an algorithm determining this precisely, something that is\nharmless at the value level (such as re-ordering constraints) could potentially\nserve as an API break. Additionally, this algorithm is a huge source of\nnon-local information, and in the presence of a large number of type variables,\nof different kinds, can easily become confusing. Having explicit foralls\nquantifying all type variables makes it clear what the order for these type\nvariables is for ``TypeApplications``, and also allows us to choose it\noptimally for our API, rather than relying on what the algorithm would produce.\nThis is significantly more convenient, and means less non-local information and\nconfusion.\n\nAdditionally, type-level programming requires significant use of 'exotic kinds',\nwhich in our case include ``Constraint -\u003e Type`` and ``Row Type``, to name but a\nfew. While GHC can (mostly) infer kind signatures, much the same way as we\nexplicitly annotate type signatures as a form of active documentation (and to\nassist the type checker when using type holes), explicitly annotating _kind_\nsignatures allows us to be clear to the users where exotic kinds are expected,\nas well as ensuring that we don't make any errors ourselves. This, together with\nexplicit foralls, essentially bring the same practices to the kind level as the\nHaskell community already considers to be good at the type level.\n\n`where` bindings are quite common in idiomatic Haskell, and quite often contain\nnon-trivial logic. They're also a common refactoring, and 'hole-driven\ndevelopment' tool, where you create a hole to be filled with a `where`-bound\ndefinition. Even in these cases, having an explicit signature on\n`where`-bindings helps: during development, you can use typed holes inside the\n`where`-binding with useful information (absent a signature, you'll get\nnothing), and it makes the code much easier to understand, especially if the\n`where`-binding is complex. It's also advantageous when 'promoting'\n`where`-binds to full top-level definitions, as the signature is already there.\nSince we need to do considerable type-level programming as part of Plutus, this\nbecomes even more important, as GHC's type inference algorithm can often fail in\nthose cases on `where`-bindings, which will sometimes fail to derive, giving a\nvery strange error message, which would need a signature to solve anyway. By\nmaking this practice proactive, we are decreasing confusion, as well as\nincreasing readability. While in theory, this standard should extend to\n`let`-bindings as well, these are much rarer, and can be given signatures with\n`::` if `ScopedTypeVariables` is on (which it is for us by default) if needed.\n\nWhile it is possible to provide definitions for multiple signatures at once at\nthe module level, it's almost never a good idea to do so. Even in fairly\nstraightforward cases (like the provided example), it can be confusing, and\nin cases where the 'definition disassembly' is more complex (or involves other\nlanguage features, such as named field puns or wildcards) definitely _is_\nconfusing. Furthemore, it's almost never warranted; it can be more concise, but\nat the cost of clarity, which is never a viable tradeoff long-term. Lastly,\ndocumentation and refactoring of such multi-definitions is more difficult as a\nresult. Keeping strictly to a 'one signature, one definition' structure aids\nreadability and maintainability, and is almost never particularly verbose\nanyway.\n\n## Other\n\nLists SHOULD NOT be field values of types; this extends to ``String``s. Instead,\n``Vector``s (``Text``s) SHOULD be used, unless a more appropriate structure exists. \nOn-chain code, due to a lack of alternatives, is one place lists can be used as\nfield values of types.\n\nPartial functions MUST NOT be defined. Partial functions SHOULD NOT be used\nexcept to ensure that another function is total (and the type system cannot be\nused to prove it). \n\nDerivations MUST use an explicit [strategy][deriving-strategies]. Thus, the \nfollowing is wrong:\n\n```haskell\nnewtype Foo = Foo (Bar Int)\n    deriving (Eq, Show, Generic, FromJSON, ToJSON)\n```\n\nInstead, write it like this:\n\n```haskell\nnewtype Foo = Foo (Bar Int)\n    deriving stock (Generic)\n    deriving newtype (Eq, Show)\n    deriving anyclass (FromJSON, ToJSON)\n```\n\nDeriving via SHOULD be preferred to newtype derivation, especially where the\nunderlying type representation could change significantly.\n\n``type`` SHOULD NOT be used. The only acceptable case is abbreviation of large\ntype-level computations. In particular, ``type`` MUST NOT be used to create an\nabstraction boundary. \n\nSum types containing record fields MUST NOT be defined. Thus, the following is\nnot allowed:\n\n```haskell\ndata Foo = Bar | Baz { quux :: Int, frob :: (Int, Int) }\n```\n\n### Justification\n\nHaskell lists are a large example of the legacy of the language: they (in the\nform of singly linked lists) have played an important role in the development of\nfunctional programming (and for some 'functional' languages, continue to do so).\nHowever, from the perspective of data structures, they are suboptimal except for\n_extremely_ specific use cases. In almost any situation involving data (rather\nthan control flow), an alternative, better structure exists. Although it is both\nacceptable and efficient to use lists within functions (due to GHC's extensive\nfusion optimizations), from the point of view of field values, they are a poor\nchoice from both an efficiency perspective, both in theory _and_ in practice.\nFor almost all cases where you would want a list field value, a ``Vector`` field\nvalue is more appropriate, and in almost all others, some other structure (such\nas a ``Map``) is even better. We make a named exception for on-chain code, as no\nalternatives presently exist.\n\nPartial functions are runtime bombs waiting to explode. The number of times the\n'impossible' happened, especially in production code, is significant in our\nexperience, and most partiality is easily solvable. Allowing the compiler to\nsupport our efforts, rather than being blind to them, will help us write more\nclear, more robust, and more informative code. Partiality is also an example of\nlegacy, and it is legacy of _considerable_ weight. Sometimes, we do need an\n'escape hatch' due to the impossibility of explaining what we want to the\ncompiler; this should be the _exception_, not the rule.\n\nDerivations are one of the most useful features of GHC, and extend the\ncapabilities of Haskell 2010 considerably. However, with great power comes great\nambiguity, especially when ``GeneralizedNewtypeDeriving`` is in use. While there\n_is_ an unambiguous choice if no strategy is given, it becomes hard to remember.\nThis is especially dire when ``GeneralizedNewtypeDeriving`` combines with\n``DeriveAnyClass`` on a newtype. Explicit strategies give more precise control\nover this, and document the resulting behaviour locally. This reduces the number\nof things we need to remember, and allows more precise control when we need it.\nLastly, in combination with ``DerivingVia``, considerable boilerplate can be\nsaved; in this case, explicit strategies are _mandatory_.\n\nThe only exception to the principle above is newtype deriving, which can\noccasionally cause unexpected problems; if we use a newtype derivation, and\nchange the underlying type, we get no warning. Since this can affect the effect\nof some type classes drastically, it would be good to have the compiler check\nour consistency.\n\n``type`` is generally a terrible idea in Haskell. You don't create an\nabstraction boundary with it (any operations on the 'underlying type' still work\nover it), and compiler output becomes _very_ inconsistent (sometimes showing the\n``type`` definition, sometimes the underlying type). If your goal is to create\nan abstraction boundary with its own operations, ``newtype`` is both cost-free\nand clearer; if that is _not_ your goal, just use the type you'd otherwise\nrename, since it's equivalent semantically. The only reasonable use of ``type``\nis to hide complex type-level computations, which would otherwise be too long.\nEven this is somewhat questionable, but the questionability comes from the\ntype-level computation being hidden, not ``type`` as such.\n\nThe combination of record syntax and sum types, while allowed, [causes\nconsiderable issues][record-sum-bad]. One of the biggest problems with this\ncombination is that is sneaks in partiality 'via the back door'; at the same\ntime, it also produces confusing warnings with `-Wno-incomplete-record-updates`\nand `record-dot-preprocessor`. While arguably convenient in some cases, this\nultimately creates more problems than it solves.\n\n# Design practices\n\n## Parse, don't validate\n\n[Boolean blindness][boolean-blindness] SHOULD NOT be used in the design of any\nfunction or API. Returning more meaningful data SHOULD be the preferred choice.\nThe general principle of ['parse, don't validate'][parse-dont-validate] SHOULD\nguide design and implementation.\n\n### Justification\n\nThe [description of boolean blindness][boolean-blindness] gives specific reasons why it is a poor\ndesign choice; additionally, it runs counter to the principle of ['parse, don't\nvalidate][parse-dont-validate]. While sometimes unavoidable, in many cases, it's\npossible to give back a more meaningful response than 'yes' or 'no, and we\nshould endeavour to do this. Designs that avoid boolean blindness are more\nflexible, less bug-prone, and allow the type checker to assist us when writing.\nThis, in turn, reduces cognitive load, improves our ability to refactor, and\nmeans fewer bugs from things the compiler _could_ have checked if a function\n_wasn't_ boolean-blind.\n\n## No multi-parameter type-classes without functional dependencies\n\nAny multi-parameter type class MUST have a functional dependency restricting its\nrelation to a one-to-many at most. In cases of true many-to-many relationships,\ntype classes MUST NOT be used as a solution to the problem.\n\n### Justification\n\nMulti-parameter type classes allow us to express more complex relationships\namong types; single-parameter type classes effectively permit us to 'subset'\n``Hask`` only. However, multi-parameter type classes make type inference\n_extremely_ flakey, as the global coherence condition can often lead to the\ncompiler being unable to determine what instance is sought even if all the type\nparameters are concrete, due to anyone being able to add a new instance at any\ntime. This is largely caused by multi-parameter type classes defaulting to\neffectively representing arbitrary many-to-many relations.\n\nWhen we do not _have_ arbitrary many-to-many relations, multi-parameter type\nclasses are useful and convenient. We can indicate this using functional\ndependencies, which inform the type checker that our relationship is not\narbitrarily many-to-many, but rather many-to-one or even one-to-one. This is a\nstandard practice in many libraries (``mtl`` being the most ubiquitous example),\nand allows us the benefits of multi-parameter type classes without making type\nchecking confusing and difficult.\n\nIn general, many-to-many relationships pose difficult design choices, for which\ntype classes are _not_ the correct solution. If a functional dependency _cannot_\nbe provided for a type class, it suggests that the current design relies\ninherently on a many-to-many relation, and should be either rethought to\neliminate it, or be dealt with using a more appropriate means.\n\n## Type classes must have laws\n\nAny type class not imported from an external dependency MUST have laws. These\nlaws MUST be documented in a Haddock comment on the type class definition, and\nall instances MUST follow these laws.\n\n### Justification\n\nType classes are a powerful feature of Haskell, but can also be its most\nconfusing. As they allow arbitrary ad-hoc polymorphism, and are globally\nvisible, it is important that we limit the confusion this can produce.\nAdditionally, type classes without laws inhibit equational reasoning, which is\none of Haskell's biggest strengths, _especially_ in the presence of what amounts\nto arbitrary ad-hoc polymorphism.\n\nAdditionally, type classes with laws allow the construction of _provably_\ncorrect abstractions above them. This is also a common feature in Haskell,\nranging from profunctor optics to folds. If we define our own type classes, we\nwant to be able to abstract above them with _total_ certainty of correctness.\nLawless type classes make this difficult to do: compare the number of\nabstractions built on `Functor` or `Traversable` as opposed to `Foldable`.\n\nThus, type classes having laws provides both ease of understanding and\nadditional flexibility.\n\n# Libraries and frameworks\n\n## Use `Type.Reflection` instead of `Data.Typeable`\n\n`Data.Typeable` from `base` SHOULD NOT be used; the only exception is for\ninterfacing with legacy libraries. Whenever its capabilities are required,\n[`Type.Reflection`][type-reflection] SHOULD be used.\n\n### Justification\n\n`Data.Typeable` was the first attempt to bring runtime type information to GHC;\nthis mechanism is necessary, as GHC normally performs type erasure. The original\ndesign of `Data.Typeable.Typeable` required the construction of a `TypeRep`,\nwhich could be user-specified. This led to issues of correctness, as\nuser-specified `TypeRep`s could easily not follow the conventions that GHC\nexpected, and also coherency, as there's no guarantee that for any given type,\nits `TypeRep` would be unique. This was later subsumed into the\n`DeriveDataTypeable` extension, which made it impossible to define `Typeable`\ninstances except through the mechanisms provided by GHC. \n\nAdditionally, as `Data.Typeable` predated `TypeApplications`, its API requires a\nvalue of a specific type to direct which `TypeRep` to provide. This suffers from\nsimilar problems as `Foreign.Storable.sizeOf`, as frequently, there is no\nsuitable value to provide. This forced developers to write code like\n\n```haskell\ntypeOf (undefined :: a)\n```\n\nThis looks strange, and isn't the approach taken by modern APIs. Lastly,\n`Data.Typeable` had to be derived for any type that wanted to use its\nmechanisms, which forced developers to 'pay' for these instances, whether they\nwanted to or not.\n\n`Type.Reflection` has been the go-to API for these purposes since GHC 8.2. It\nimproves the situation with `Data.Typeable` by replacing the old mechanism with\na compiler-generated singleton. Furthermore, deriving `Typeable` is now\nunnecessary, much in the same way as deriving `Coercible` is not necessary: GHC\nhandles all of this. Additionally, the API is now based on `TypeApplications`,\nwhich allows us to write \n\n```haskell\ntypeRep @a\n```\n\nThe system is also entirely pay-as-you-go - instead of the responsibility being\nplaced on the data types (thus requiring you to pay the cost of the instances\nwhether you needed them or not), the responsibility is now on the functions that\nconsume them: if you specify a `(Typeable a) =\u003e` constraint, this informs GHC\nthat the singleton for `TypeRep a` is needed in this function, but not anywhere\nelse. \n\nSince `Type.Reflection` can do everything `Data.Typeable` can, has a more modern\nAPI, and also lower cost, there is no reason to use `Data.Typeable` anymore\nexcept for legacy compatibility reasons.\n\n[pvp]: https://pvp.haskell.org/\n[policeman]: https://hackage.haskell.org/package/policeman\n[haddock-since]: https://haskell-haddock.readthedocs.io/en/latest/markup.html#since\n[bird-tracks]: https://haskell-haddock.readthedocs.io/en/latest/markup.html#code-blocks\n[hedgehog-classes]: http://hackage.haskell.org/package/hedgehog-classes\n[hspec-hedgehog]: http://hackage.haskell.org/package/hspec-hedgehog\n[property-based-testing]: https://dl.acm.org/doi/abs/10.1145/1988042.1988046\n[hedgehog]: http://hackage.haskell.org/package/hedgehog\n[deriving-strategies]: https://gitlab.haskell.org/ghc/ghc/-/wikis/commentary/compiler/deriving-strategies\n[functor-parametricity]: https://www.schoolofhaskell.com/user/edwardk/snippets/fmap\n[alexis-king-options]: https://lexi-lambda.github.io/blog/2018/02/10/an-opinionated-guide-to-haskell-in-2018/#warning-flags-for-a-safe-build\n[hlint]: http://hackage.haskell.org/package/hlint\n[fourmolu]: http://hackage.haskell.org/package/fourmolu\n[rfc-2119]: https://tools.ietf.org/html/rfc2119\n[boolean-blindness]: http://dev.stephendiehl.com/hask/#boolean-blindness\n[parse-dont-validate]: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/\n[hspec]: http://hackage.haskell.org/package/hspec\n[rdp]: https://hackage.haskell.org/package/record-dot-preprocessor\n[rdp-issue]: https://github.com/ghc-proposals/ghc-proposals/pull/282\n[type-reflection]: https://hackage.haskell.org/package/base-4.15.0.0/docs/Type-Reflection.html\n[record-sum-bad]: https://stackoverflow.com/a/37657296/2629787\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmlabs-haskell%2Fstyleguide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmlabs-haskell%2Fstyleguide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmlabs-haskell%2Fstyleguide/lists"}