[
  {
    "path": "README.md",
    "content": "# Haskell's Dangerous Functions\n\n## What does dangerous mean?\n\nDangerous could mean either of these:\n\n- Partial: can throw exceptions in pure code\n- Unsafe: can cause segfaults\n- Has unexpected performance characteristics\n- Doesn't do what you want\n- Doesn't do what you think it does\n\n## How to forbid these dangerous functions in your codebase\n\n1. Copy the `hlint.yaml` file in this repository to `.hlint.yaml` within your repository\n\n    ```\n    cat /path/to/haskell-dangerous-functions >> /path/to/your/project/.hlint.yaml\n    ```\n\n2. Run `hlint` on your code.\n   Make sure to require new changes to be `hlint`-clean.\n   You can use `hlint --default` to generate a settings file ignoring all the hints currently outstanding.\n   You can use [pre-commit hooks](https://pre-commit.com/) to forbid committing non-`hlint`-clean changes.\n\n3. Whenever you want to make an exception, and use a forbidden function anyway, use the `ignore` key to add an exception to the `.hlint.yaml` file.\n\n\n## FAQ\n\n\n* **It seems pretty silly that these functions still exist, and their dangers not well-documented.**\n\n  I know! See [the relevant discussion on the GHC issue tracker](https://gitlab.haskell.org/ghc/ghc/-/merge_requests/5206).\n\n* **Surely everyone knows about these?**\n\n  Maybe, but I certainly didn't, until they caused real production issues.\n\n## Contributing\n\n**WANTED: Evidence of the danger in these functions.**\nIf you can showcase a public incident with real-world consequences that happened because of one of these functions, I would love to refer to it in this document!\n\n\nIf you know about another dangerous function that should be avoided, feel free to submit a PR!\nPlease include:\n\n* an `hlint` config to forbid the function in [`hlint.yaml`](hlint.yaml).\n* a section in this document with:\n  * Why the function is dangerous\n  * A reproducible way of showing that it is dangerous.\n  * An alternative to the dangerous function\n\nIt might be that the function you have in mind is not dangerous but still weird.\nIn that case you can add it to [the Haskell WAT list](https://github.com/NorfairKing/haskell-WAT).\n\n\n\n## Overview of the dangerous functions\n\n\n### [`forkIO`](https://hackage.haskell.org/package/base-4.14.1.0/docs/Control-Concurrent.html#v:forkIO)\n\nTL;DR: Using `forkIO` is _VERY_ hard to get right, use the async library instead.\n\nThe main issue is that when threads spawned using `forkIO` throw an exception, this exception is not rethrown in the thread that spawned that thread.\n\nAs an example, suppose we `forkIO` a server and something goes wrong.\nThe main thread will not notice that anything went wrong.\nThe only indication that an exception was thrown will be that something is printed on `stderr`.\n\n``` haskell\n$ cat test.hs\n#!/usr/bin/env stack\n-- stack --resolver lts-15.15 script\n{-# LANGUAGE NumericUnderscores #-}\nimport Control.Concurrent\nmain :: IO ()\nmain = do\n  putStrLn \"Starting our 'server'.\"\n  forkIO $ do\n    putStrLn \"Serving...\"\n    threadDelay 1_000_000\n    putStrLn \"Oh no, about to crash!\"\n    threadDelay 1_000_000\n    putStrLn \"Aaaargh\"\n    undefined\n  threadDelay 5_000_000\n  putStrLn \"Still running, eventhough we crashed\"\n  threadDelay 5_000_000\n  putStrLn \"Ok that's enough of that, stopping here.\"\n```\n\nWhich outputs:\n\n```\n$ ./test.hs\nStarting our 'server'.\nServing...\nOh no, about to crash!\nAaaargh\ntest.hs: Prelude.undefined\nCallStack (from HasCallStack):\n  error, called at libraries/base/GHC/Err.hs:80:14 in base:GHC.Err\n  undefined, called at /home/syd/test/test.hs:17:5 in main:Main\nStill running, eventhough we crashed\nOk that's enough of that, stopping here.\n```\n\nInstead, we can use [`concurrently_` from the `async` package](http://hackage.haskell.org/package/async-2.2.3/docs/Control-Concurrent-Async.html#v:concurrently_):\n\n``` haskell\n$ cat test.hs\n-- stack --resolver lts-15.15 script\n\n{-# LANGUAGE NumericUnderscores #-}\n\nimport Control.Concurrent\nimport Control.Concurrent.Async\n\nmain :: IO ()\nmain = do\n  putStrLn \"Starting our 'server'.\"\n  let runServer = do\n        putStrLn \"Serving...\"\n        threadDelay 1_000_000\n        putStrLn \"Oh no, about to crash!\"\n        threadDelay 1_000_000\n        putStrLn \"Aaaargh\"\n        undefined\n  let mainThread = do\n        threadDelay 5_000_000\n        putStrLn \"Still running, eventhough we crashed\"\n        threadDelay 5_000_000\n        putStrLn \"Ok that's enough of that, stopping here.\"\n  concurrently_ runServer mainThread\n```\n\nto output:\n\n```\n$ ./test.hs\nStarting our 'server'.\nServing...\nOh no, about to crash!\nAaaargh\ntest.hs: Prelude.undefined\nCallStack (from HasCallStack):\n  error, called at libraries/base/GHC/Err.hs:80:14 in base:GHC.Err\n  undefined, called at /home/syd/test.hs:18:9 in main:Main\n```\n\nSee also:\n\n* https://github.com/informatikr/hedis/issues/165\n* https://github.com/hreinhardt/amqp/issues/96\n\n### [`forkProcess`](https://hackage.haskell.org/package/unix-2.7.2.2/docs/System-Posix-Process.html#v:forkProcess)\n\nMostly impossible to get right.\nYou probably want to be using [the `async` library](http://hackage.haskell.org/package/async) instead.\n\nIf you think \"I know what I'm doing\" then you're probably still wrong.\nRethink what you're doing entirely.\n\nSee also https://www.reddit.com/r/haskell/comments/jsap9r/how_dangerous_is_forkprocess/\n\n### Partial functions\n\n#### [`head`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Data-List.html#v:head)\n\nThrows an exception in pure code when the input is an empty list.\n\n```\nPrelude> head []\n*** Exception: Prelude.head: empty list\n```\n\nUse `listToMaybe` instead.\n\nApplies to [Data.Text.head](https://hackage.haskell.org/package/text-1.2.3.2/docs/Data-Text.html#v:head) as well\n\nTrail of destruction:\n\n* [`Prelude.head: empty list` makes hls unusable until restart](https://github.com/haskell/haskell-language-server/issues/1618)\n\n#### [`tail`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Data-List.html#v:tail)\n\nThrows an exception in pure code when the input is an empty list.\n\n```\nPrelude> tail []\n*** Exception: Prelude.tail: empty list\n```\n\nUse `drop 1` or a case-match instead.\n\nApplies to [Data.Text.tail](https://hackage.haskell.org/package/text-1.2.3.2/docs/Data-Text.html#v:tail) as well\n\n#### [`init`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Data-List.html#v:init)\n\nThrows an exception in pure code when the input is an empty list.\n\n```\nPrelude> init []\n*** Exception: Prelude.init: empty list\n```\n\nUse a case-match on the `reverse` of the list instead, but keep in mind that it uses linear time in the length of the list.\nUse a different data structure if that is an issue for you. \nSince `base-4.19` you can also employ [`unsnoc`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:unsnoc).\n\nApplies to [Data.Text.init](https://hackage.haskell.org/package/text-1.2.3.2/docs/Data-Text.html#v:init) as well\n\n#### [`last`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Data-List.html#v:last)\n\nThrows an exception in pure code when the input is an empty list.\n\n```\nPrelude> last []\n*** Exception: Prelude.last: empty list\n```\n\nUse a `listToMaybe . reverse` instead, but keep in mind that it uses linear time in the length of the list.\nUse a different data structure if that is an issue for you.\nSince `base-4.19` you can also employ [`unsnoc`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:unsnoc).\n\nApplies to [Data.Text.last](https://hackage.haskell.org/package/text-1.2.3.2/docs/Data-Text.html#v:last) as well\n\n#### [`'!!'`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Data-List.html#v:-33--33-)\n\nThrows an exception in pure code when the index is out of bounds.\n\n```\nPrelude> [1, 2, 3] !! 3\n*** Exception: Prelude.!!: index too large\n```\n\nIt also allows negative indices, for which it also throws.\n\n```\nPrelude> [1,2,3] !! (-1)\n*** Exception: Prelude.!!: negative index\n```\n\nThe right way to index is to not use a list, because list indexing takes `O(n)` time, even if you find a safe way to do it.\nIf you _really_ need to deal with _list_ indexing (you don't), then you can use a combination of [`take`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Data-List.html#v:take) and [`drop`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Data-List.html#v:drop)\nor (since `base-4.19`) [`'!?'`](https://hackage.haskell.org/package/base/docs/Data-List.html#v:-33--63-).\n\n#### [`fromJust`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Data-Maybe.html#v:fromJust)\n\nThrows an exception _in pure code_ when the input is `Nothing`.\n\n```\nPrelude Data.Maybe> fromJust Nothing\n*** Exception: Maybe.fromJust: Nothing\nCallStack (from HasCallStack):\n  error, called at libraries/base/Data/Maybe.hs:148:21 in base:Data.Maybe\n  fromJust, called at <interactive>:11:1 in interactive:Ghci1\n```\n\nUse a case-match instead.\n\n#### [`read`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Text-Read.html#v:read)\n\nThere are multiple reasons not to use `read`.\nThe most obvious one is that it is partial.\nIt throws an exception _in pure code_ whenever the input cannot be parsed (and doesn't even give a helpful parse error):\n\n```\nPrelude> read \"a\" :: Int\n*** Exception: Prelude.read: no parse\n```\n\nYou can use `readMaybe` to get around this issue, HOWEVER:\n\nThe second reason not to use `read` is that it operates on `String`.\n\n``` haskell\nread :: Read a => String -> a\n```\n\nIf you are doing any parsing, you should be using a more appropriate data type to parse: (`Text` or `ByteString`)\n\nThe third reason is that `read` comes from [the `Read` type class](https://hackage.haskell.org/package/base/docs/Text-Read.html#t:Read), which has no well-defined semantics.\nIn an ideal case, `read` and `show` would be inverses but this is _just not the reality_.\nSee [`UTCTime`](https://hackage.haskell.org/package/time/docs/Data-Time-Clock.html#t:UTCTime) as an example.\n\n#### [`toEnum`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Prelude.html#v:toEnum)\n\nThe `toEnum :: Enum => Int -> a` function is partial whenever the `Enum`erable type `a` is smaller than `Int`:\n\n```\nPrelude> toEnum 5 :: Bool\n*** Exception: Prelude.Enum.Bool.toEnum: bad argument\nPrelude Data.Word> toEnum 300 :: Word8\n*** Exception: Enum.toEnum{Word8}: tag (300) is outside of bounds (0,255)\n```\n\n#### [`succ`](https://hackage.haskell.org/package/base-4.15.0.0/docs/GHC-Enum.html#v:succ) and [`pred`](https://hackage.haskell.org/package/base-4.15.0.0/docs/GHC-Enum.html#v:pred)\n\nThese are partial, on purpose.\nAccording to the docs:\n\n> The calls `succ maxBound` and `pred minBound` should result in a runtime error.\n\n```\nPrelude Data.Word> succ 255 :: Word8\n*** Exception: Enum.succ{Word8}: tried to take `succ' of maxBound\nPrelude Data.Word> pred 0 :: Word8\n*** Exception: Enum.pred{Word8}: tried to take `pred' of minBound\n```\n\nUse something like [`succMay`](https://hackage.haskell.org/package/safe-0.3.19/docs/Safe.html#v:succMay).\n\n#### Functions involving division\n\n* [`quot`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Prelude.html#v:quot)\n* [`div`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Prelude.html#v:div)\n* [`rem`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Prelude.html#v:rem)\n* [`mod`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Prelude.html#v:mod)\n* [`divMod`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Prelude.html#v:divMod)\n* [`quotRem`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Prelude.html#v:quotRem)\n\n```\nPrelude> quot 1 0\n*** Exception: divide by zero\nPrelude> minBound `quot` (-1) :: Int\n*** Exception: arithmetic overflow\nPrelude> div 1 0\n*** Exception: divide by zero\nPrelude> minBound `div` (-1) :: Int\n*** Exception: arithmetic overflow\nPrelude> rem 1 0\n*** Exception: divide by zero\nPrelude> mod 1 0\n*** Exception: divide by zero\nPrelude> divMod 1 0\n*** Exception: divide by zero\nPrelude> quotRem 1 0\n*** Exception: divide by zero\n```\n\nWhenever you consider using division, _really_ ask yourself whether you need division.\nFor example, you can (almost always) replace ``a `div` 2 <= b`` by `a <= 2 * b`.\n(If you're worried about overflow, then use a bigger type.)\n\nIf your use-case has a fixed (non-`0`) _literal_ denominator, like ``a `div` 2``, and you have already considered using something other than division, then your case constitutes an acceptable exception.\n\nNote that integer division may not be what you want in the first place anyway:\n\n```\nPrelude> 5 `div` 2\n2 -- Not 2.5\n```\n\nSee also https://github.com/NorfairKing/haskell-WAT#num-int\n\n#### [`minimum`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Data-List.html#v:minimum) and [`maximum`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Data-List.html#v:maximum)\n\nThese functions throw an exception in pure code whenever the input is empty:\n\n```\nPrelude> minimum []\n*** Exception: Prelude.minimum: empty list\nPrelude> maximum []\n*** Exception: Prelude.maximum: empty list\nPrelude> minimum Nothing\n*** Exception: minimum: empty structure\nPrelude> minimum (Left \"wut\")\n*** Exception: minimum: empty structure\nPrelude Data.Functor.Const> minimum (Const 5 :: Const Int ())\n*** Exception: minimum: empty structure\n```\n\nThe same goes for [`minimumBy`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Data-List.html#v:minimumBy) and [`maximumBy`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Data-List.html#v:maximumBy).\n\nYou can use [`minimumMay`](http://hackage.haskell.org/package/safe-0.3.19/docs/Safe.html#v:minimumMay) from the [`safe`](http://hackage.haskell.org/package/safe) package (or a case-match on the `sort`-ed version of your list, if you don't want an extra dependency).\n\nApplies to [Data.Text.maximum](https://hackage.haskell.org/package/text-1.2.3.2/docs/Data-Text.html#v:maximum) and [Data.Text.minimum](https://hackage.haskell.org/package/text-1.2.3.2/docs/Data-Text.html#v:minimum) as well\n\n#### [`Data.Text.Encoding.decodeUtf8`](http://hackage.haskell.org/package/text-1.2.4.1/docs/Data-Text-Encoding.html#v:decodeUtf8)\n\nThrows on invalid UTF-8 datao use `Data.Text.Encoding.decodeUtf8'` instead.\n\n### Functions that throw exceptions in pure code on purpose\n\n#### [`throw`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Control-Exception.html#v:throw)\n\nPurposely throws an exception _in pure code_.\n\n```\nPrelude Control.Exception> throw $ ErrorCall \"here be a problem\"\n*** Exception: here be a problem\n```\n\nDon't throw from pure code, use throwIO instead.\n\n#### [`undefined`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Prelude.html#v:undefined)\n\nPurposely fails, with a particularly unhelpful error message.\n\n```\nPrelude> undefined\n*** Exception: Prelude.undefined\nCallStack (from HasCallStack):\n  error, called at libraries/base/GHC/Err.hs:80:14 in base:GHC.Err\n  undefined, called at <interactive>:1:1 in interactive:Ghci1\n```\n\nDeal with errors appropriately instead.\n\nAlso see `error` below.\n\n#### [`error`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Prelude.html#v:error)\n\nPurposely fails, with an only slightly less unhelpful error message than `undefined`.\n\n```\nPrelude> error \"here be a problem\"\n*** Exception: here be a problem\nCallStack (from HasCallStack):\n  error, called at <interactive>:4:1 in interactive:Ghci1\n```\n\nDeal with errors appropriately instead.\n\nIf you're _really very extra sure_ that a certain case will never happen.\nBubble up the error to the `IO` part of your code and then use `throwIO` or `die`.\n\n\n### Functions that do unexpected things\n\n#### [`realToFrac`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Prelude.html#v:realToFrac)\n\nThis function goes through `Rational`:\n\n```\n-- | general coercion to fractional types\nrealToFrac :: (Real a, Fractional b) => a -> b\nrealToFrac = fromRational . toRational\n```\n\n`Rational` does not have all the values that a `Real` like `Double` might have, so things will go wrong in ways that you don't expect:\n\n```\nPrelude> realToFrac nan :: Double\n-Infinity\n```\n\nAvoid general coercion functions and anything to do with `Double` in particular.\n\nSee also https://github.com/NorfairKing/haskell-WAT#real-double\n\n#### [`%`](https://hackage.haskell.org/package/base-4.16.1.0/docs/Data-Ratio.html#v:-37-): Rational values\n\nThe `%` function is used to construct rational values:\n\n``` haskell\ndata Ratio a = !a :% !a  deriving Eq\n```\n``` haskell\nPrelude Data.Int Data.Ratio> 1 % 12 :: Ratio Int8\n1 % 12\n```\n\nThere are constraints on the two values in Rational values:\n\nRecall (from the docs); \"The numerator and denominator have no common factor and the denominator is positive.\"\n\nWhen using fixed-size underlying types, you can end up with invalid `Ratio` values using `Num` functions:\n\n```\nPrelude Data.Int Data.Ratio> let r = 1 % 12 :: Ratio Int8\nPrelude Data.Int Data.Ratio> r - r\n0 % (-1)\nPrelude Data.Int Data.Ratio> r + r\n3 % (-14)\n> r * r\n1 % (-112)\n```\n\nWhen using arbitrarily-sized underlying types, you can end up with arbitrary runtime:\n\n```\n(1 % 100)^10^10^10 :: Rational -- Prepare a way to kill this before you try it out.\n```\n\n`Ratio` values create issues for any underlying type, so avoid them.\nConsider whether you really need any rational values.\nIf you _really_ do, and you have a clear maximum value, consider using fixed-point values.\nIf that does not fit your use-case, consider using `Double` with all its caveats.\n\n#### [`fromIntegral`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Prelude.html#v:fromIntegral) and [`fromInteger`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Prelude.html#v:fromInteger)\n\n`fromIntegral` has no constraints on the size of the output type, so that output type could be smaller than the input type.\nIn such a case, it performs silent truncation:\n```\n> fromIntegral (300 :: Word) :: Word8\n44\n```\nSimilarly for `fromInteger`:\n```\n> fromInteger 300 :: Word8\n44\n```\n\n`fromIntegral` has also had some very nasty bugs that involved the function behaving differently (even partially) depending on optimisation levels.\nSee [GHC #20066](https://gitlab.haskell.org/ghc/ghc/-/issues/20066) and [GHC #19345](https://gitlab.haskell.org/ghc/ghc/-/issues/19345).\n\nAvoid general coercion functions but write specific ones instead, as long as the type of the result is bigger than the type of the input.\n\n```\nword32ToWord64 :: Word32 -> Word64\nword32ToWord64 = fromIntegral -- Safe because Word64 is bigger than Word32\n```\n\nPrefer to use functions with non-parametric types and/or functions that fail loudly, like these:\n\n* [`naturalToInteger :: Natural -> Integer`](https://hackage.haskell.org/package/base-4.15.0.0/docs/GHC-Natural.html#v:naturalToInteger)\n* [`naturalToWord :: Natural -> Maybe Word`](https://hackage.haskell.org/package/base-4.15.0.0/docs/GHC-Natural.html#v:naturalToWordMaybe)\n* [`toIntegralSized`](http://hackage.haskell.org/package/base-4.14.0.0/docs/Data-Bits.html#v:toIntegralSized)\n\n\nWitness the trail of destruction:\n\n* [Bug in `System.IO.hWaitForInput`](http://haskell.1045720.n5.nabble.com/Deprecating-fromIntegral-tt5864578.html) because of `fromIntegral`\n* [Bug in cryptography-related code](https://github.com/haskell-crypto/cryptonite/issues/330) because of `fromIntegral`\n\nI was also pointed to the [`finitary`](http://hackage.haskell.org/package/finitary) package but I haven't used it yet.\n\n#### [`toEnum`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Prelude.html#v:toEnum)\n\nThe `toEnum` function suffers from the following problem **on top of being partial**.\n\nSome instances of `Enum` use \"the next constructor\" as the next element while others use a `n+1` variant:\n\n```\nPrelude> toEnum 5 :: Double\n5.0\nPrelude Data.Fixed> toEnum 5 :: Micro\n0.000005\n```\n\nDepending on what you expected, one of those doesn't do what you think it does.\n\n#### [`fromEnum`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Prelude.html#v:fromEnum)\n\nFrom the docs:\n\n> It is implementation-dependent what fromEnum returns when applied to a value that is too large to fit in an Int.\n\nFor example, some `Integer` that does not fit in an `Int` will be mapped to `0`, some will be mapped all over the place\n\n```\nPrelude> fromEnum (2^66 :: Integer) -- To 0\n0\nPrelude> fromEnum (2^65 :: Integer) -- To 0\n0\nPrelude> fromEnum (2^64 :: Integer) -- To 0\n0\nPrelude> fromEnum (2^64 -1 :: Integer) -- To -1 ?!\n0\nPrelude> fromEnum (2^63 :: Integer) -- To -2^63 ?!\n-9223372036854775808\n```\n\nThis is because `fromEnum :: Integer -> Int` is implemented using [`integerToInt`](https://hackage.haskell.org/package/integer-gmp-0.5.1.0/docs/GHC-Integer.html) which treats big integers and small integers differently.\n\n#### [`succ`](https://hackage.haskell.org/package/base-4.15.0.0/docs/GHC-Enum.html#v:succ) and [`pred`](https://hackage.haskell.org/package/base-4.15.0.0/docs/GHC-Enum.html#v:pred)\n\nThese suffer from the same problem as `toEnum` (see above) **on top of being partial**.\n\n```\nPrelude> succ 5 :: Double\n6.0\nPrelude Data.Fixed> succ 5 :: Micro\n5.000001\nPrelude> pred 0 :: Word\n*** Exception: Enum.pred{Word}: tried to take `pred' of minBound\nPrelude Data.Ord Data.Int> succ (127 :: Int8)\n*** Exception: Enum.succ{Int8}: tried to take `succ' of maxBound\n```\n\n#### `fromString` on `ByteString`\n\nWhen converting to `ByteString`, `fromString` silently truncates to the bottom eight bits, turning your string into garbage.\n\n```haskell\n> print \"⚠\"\n\"\\9888\"\n> print (fromString \"⚠\" :: ByteString)\n\"\\160\"\n```\n\n### The [`enumFromTo`](https://hackage.haskell.org/package/base-4.15.0.0/docs/GHC-Enum.html#v:enumFromTo)-related functions\n\nThese also suffer from the same problem as `toEnum` (see above)\n\n```\nPrelude> succ 5 :: Int\n6\nPrelude Data.Fixed> succ 5 :: Micro\n5.000001\n```\n\n### Functions related to `String`-based IO\n\n#### Input\n\n- `System.IO.getChar`\n- `System.IO.getLine`\n- `System.IO.getContents`\n- `System.IO.interact`\n- `System.IO.readIO`\n- `System.IO.readLn`\n- `System.IO.readFile`\n\nThese behave differently depending on env vars, and actually fail on non-text data in files:\n\n```\nPrelude> readFile \"example.dat\"\n*** Exception: Test/A.txt: hGetContents: invalid argument (invalid byte sequence) \"\\226\\8364\n```\n\nSee also [this blogpost](https://www.snoyman.com/blog/2016/12/beware-of-readfile/).\n\nUse `ByteString`-based input and then use `Data.Text.Encoding.decodeUtf8'` if necessary. (But not `Data.Text.Encoding.decodeUtf8`, see above.)\n\n#### Output\n\n- `System.IO.putChar`\n- `System.IO.putStr`\n- `System.IO.putStrLn`\n- `System.IO.print`\n- `System.IO.writeFile`\n- `System.IO.appendFile`\n\nThese behave differently depending on env vars:\n\n```\n$ ghci\nPrelude> putStrLn \"\\973\"\nύ\n```\n\nbut\n```\n$ export LANG=C\n$ export LC_ALL=C\n$ ghci\nPrelude> putStrLn \"\\973\"\n?\n```\n\n\nUse `ByteString`-based output, on encoded `Text` values or directly on bytestrings instead.\n\n`writeFile` caused a real-world outage for @tomjaguarpaw on 2021-09-24.\n\nSee also [this blogpost](https://www.snoyman.com/blog/2016/12/beware-of-readfile/).\n\n### Functions related to `Text`-based IO\n\n- `Data.Text.IO.readFile`\n- `Data.Text.IO.Lazy.readFile`\n\nThese have the same issues as `readFile`.\n\nSee also [this blogpost](https://www.snoyman.com/blog/2016/12/beware-of-readfile/).\n\nSince `text-2.1` one can replace `Data.Text.IO` with [`Data.Text.IO.Utf8`](https://hackage.haskell.org/package/text-2.1/docs/Data-Text-IO-Utf8.html).\n\n\n### Functions with unexpected performance characteristics\n\n#### `nub`\n\n`O(n^2)`, use [`ordNub`](https://github.com/nh2/haskell-ordnub) instead\n\nTrail of destruction:\nhttps://gitlab.haskell.org/ghc/ghc/-/issues/8173#note_236901\n\n#### `foldl` and `foldMap`\n\nLazy. Use `foldl'` and `foldMap'` instead.\n\nSee [this excellent explanation](https://github.com/hasura/graphql-engine/pull/2933#discussion_r328821960).\n\n#### `sum` and `product`\nLazy accumulator, but is fixed as of [GHC 9.0.1](https://gitlab.haskell.org/ghc/ghc/-/merge_requests/4675).\n\n#### `genericLength`\n\n`genericLength` consumes O(n) stack space when returning a strict numeric type.  Lazy numeric types (e.g. `data Nat = Z | S Nat`) are *very* rare in practice so `genericLength` is probably not what you want.\n\n\n### Confusing functions\n\nThese functions are a _bad idea_ for no other reason than readability.\nIf there is a bug that involves these functions, it will be really easy to read over them.\n\n#### `unless`\n\n\n`unless` is defined as follows:\n\n```\nunless p s        =  if p then pure () else s\n```\n\nThis is really confusing in practice use [`when`](https://hackage.haskell.org/package/base-4.15.0.0/docs/Control-Monad.html#v:when) with `not` instead.\n\n#### `either`\n\nEither takes two functions as arguments, one for the `Left` case and one for the `Right` case.\nWhich comes first? I don't know either, just use a case-match instead.\n\n\n### Modules or packages to avoid\n\nThese are debatable, but requiring a good justification for using them is a good default.\n\n#### [`Control.Lens`](https://hackage.haskell.org/package/lens)\n\nThe `lens` package is full of abstract nonsense and obscure operators.\nThere are good reasons (in exceptional cases) for using it, like in [`cursor`](https://github.com/NorfairKing/cursor), for example, but it should be avoided in general.\n\nIt also has an ENORMOUS dependency footprint.\n\nIf you need to use a dependency that uses lenses without the `lens` dependency, you can use `microlens` to stick with the (relatively) simple parts of lenses.\nIf you need to use a dependency that uses `lens`, go ahead and use `lens`, but stick with `view` (`^.`) and `set` (`.~`).\n\n### Extensions to avoid\n\nThese are also debatable and low priority compared to the rest in this document, but still interesting to consider\n\n#### [`{-# LANGUAGE DeriveAnyClass #-}`](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/derive_any_class.html#extension-DeriveAnyClass)\n\nJust use\n\n``` haskell\ndata MyExample\ninstance MyClass MyExample\n```\n\ninstead of\n\n``` haskell\ndata MyExample\n  deriving MyClass\n```\n\n[GHC (rather puzzlingly) gives the recommendation to turn on `DeriveAnyClass` even when that would lead to code that throws an exception in pure code at runtime.](https://gitlab.haskell.org/ghc/ghc/-/issues/19692)\nAs a result, banning this extension brings potentially great upside: preventing a runtime exception, as well as reducing confusion, for the cost of writing a separate line for an instance if you know what you are doing.\n\nSee also [this great explainer video by Tweag](https://www.youtube.com/watch?v=Zdne-Ch2000).\n\n#### [`{-# LANGUAGE TupleSections #-}`](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/tuple_sections.html)\n\nThis lets you add `{-# LANGUAGE TupleSections #-}` and potential confusion to write `(, v)` instead of `\\a -> (a, v)`.\n\nWhenever you feel the need to use `TupleSections`, you probably want to be using a data type instead of tuples instead.\n\n#### [`{-# LANGUAGE DuplicateRecordFields #-}`](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/duplicate_record_fields.html)\n\nTo keep things simple, use prefix-named record fields like this:\n\n``` haskell\ndata Template = Template { templateName :: Text, templateContents :: Text }\n```\n\ninstead of this\n\n``` haskell\n{-# LANGUAGE DuplicateRecordFields #-}\ndata Template = Template { name :: Text, contents :: Text }\n```\n\nIt may be more typing but it makes code a lot more readable.\n\nIf you are concerned about not being able to auto-derive `aeson`'s `ToJSON` and `FromJSON` instances anymore,\nyou shouldn't be. You can still that using something like [`aeson-casing`](https://hackage.haskell.org/package/aeson-casing).\nIt's also dangerous to have serialisation output depend on the naming of things in your code, so be sure to test your serialisation with both property tests via [`genvalidity-sydtest-aeson`](https://hackage.haskell.org/package/genvalidity-sydtest-aeson) and golden tests via [`sydtest-aeson`](https://github.com/NorfairKing/sydtest).\n\n\n#### [`{-# LANGUAGE NamedFieldPuns #-}`](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/record_puns.html#extension-NamedFieldPuns)\n\nIntroduces unnecessary syntax.\n\nFor this example:\n``` haskell\ndata C = C { a :: Int }\n```\n\njust use this:\n\n\n``` haskell\nf c = foo (a c)\n```\n\ninstead of this:\n\n``` haskell\nf (C {a}) = foo a\n```\n\n#### [`{-# LANGUAGE OverloadedLabels #-}`](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/overloaded_labels.html#extension-OverloadedLabels)\n\nIf you're using this, you either know what you're doing - in which case you should know better than to use this - or you don't - in which case you definitely shouldn't use it.\nKeep your code simple and just use record field selectors instead.\n\nThis extension often goes hand in hand with lens usage, which should also be discouraged, see above.\n\n### Unsafe functions\n#### `unsafePerformIO`\n\nBefore you use this function, first read [its documentation](http://hackage.haskell.org/package/base-4.14.1.0/docs/System-IO-Unsafe.html#v:unsafePerformIO) carefully.\nIf you've done (and I know you haven't, you liar) and still want to use it, read the following section first.\n\n\nWhen you use `unsafePerformIO`, you pinkie-promise that the code in the `IO a` that you provide is definitely always 100% pure, you swear.\nIf this is not the case, all sorts of assumptions don't work anymore.\nFor example, if the code that you want to execute in `unsafePerformIO` is not evaluated, then the IO is never executed:\n\n``` haskell\nPrelude> fmap (const 'o') $ putStrLn \"hi\"\nhi\n'o'\nPrelude System.IO.Unsafe> const 'o' $ unsafePerformIO $ putStrLn \"hi\"\n'o'\n```\n\nAnother issue is that pure code can be inlined whereas IO-based code cannot. When you pinkie-promise that your code is \"morally\" pure, you also promise that inlining it will not cause trouble.\nThis is not true in general:\n\n```\nPrelude System.IO.Unsafe> let a = unsafePerformIO $ putStrLn \"hi\"\nPrelude System.IO.Unsafe> a\nhi\n()\nPrelude System.IO.Unsafe> a\n()\n```\n\nLastly, this function is also not type-safe, as you can see here:\n\n```\n$ cat file.hs\n```\n\n``` haskell\nimport Data.IORef\nimport System.IO.Unsafe\n\ntest :: IORef [a]\ntest = unsafePerformIO $ newIORef []\n\nmain = do\n  writeIORef test [42]\n  bang <- readIORef test\n  print $ map (\\f -> f 5 6) (bang :: [Int -> Int -> Int])\n```\n\n```\n$ runhaskell file.hs\n[file.hs: internal error: stg_ap_pp_ret\n    (GHC version 8.8.4 for x86_64_unknown_linux)\n    Please report this as a GHC bug:  https://www.haskell.org/ghc/reportabug\n[1]    13949 abort (core dumped)  runhaskell file.hs\n```\n\n#### [`unsafeDupablePerformIO`](http://hackage.haskell.org/package/base-4.14.1.0/docs/System-IO-Unsafe.html#v:unsafeDupablePerformIO)\n\nLike `unsafePerformIO` but is even less safe.\n\n#### [`unsafeInterleaveIO`](http://hackage.haskell.org/package/base-4.14.1.0/docs/System-IO-Unsafe.html#v:unsafeInterleaveIO)\n\nUsed to define lazy IO, which should be avoided.\n\n#### [`unsafeFixIO`](http://hackage.haskell.org/package/base-4.14.1.0/docs/System-IO-Unsafe.html#v:unsafeFixIO)\n\nUnsafe version of `fixIO`.\n\n#### [`unsafeCoerce`](https://hackage.haskell.org/package/base-4.21.0.0/docs/Unsafe-Coerce.html#v:unsafeCoerce)\n\nCan cause segfaults. All bets are off:\n\n```\nghci> (unsafeCoerce (5 :: Int) :: (Int -> String)) 5\n\"[1]    3415638 segmentation fault (core dumped)  ghci\n```\n\n### Deprecated\n\n#### `return`\n\nUse `pure` instead.\nSee https://gitlab.haskell.org/ghc/ghc/-/wikis/proposal/monad-of-no-return\n\n"
  },
  {
    "path": "concat-benchmark.hs",
    "content": "#!/usr/bin/env stack\n-- stack --resolver lts-16.31 script\n\nimport Control.DeepSeq\nimport Criterion.Main\n\n-- Concat looks linear\n-- benchmarking concat/256\n-- time                 2.474 μs   (2.466 μs .. 2.484 μs)\n--                      1.000 R²   (1.000 R² .. 1.000 R²)\n-- mean                 2.485 μs   (2.472 μs .. 2.498 μs)\n-- std dev              45.33 ns   (37.19 ns .. 55.72 ns)\n-- variance introduced by outliers: 19% (moderately inflated)\n--\n-- benchmarking concat/1024\n-- time                 10.45 μs   (10.35 μs .. 10.57 μs)\n--                      0.999 R²   (0.999 R² .. 1.000 R²)\n-- mean                 10.41 μs   (10.33 μs .. 10.48 μs)\n-- std dev              243.1 ns   (204.8 ns .. 296.0 ns)\n-- variance introduced by outliers: 25% (moderately inflated)\n--\n-- benchmarking concat/4096\n-- time                 41.92 μs   (41.67 μs .. 42.23 μs)\n--                      1.000 R²   (0.999 R² .. 1.000 R²)\n-- mean                 42.02 μs   (41.76 μs .. 42.33 μs)\n-- std dev              991.6 ns   (790.8 ns .. 1.260 μs)\n-- variance introduced by outliers: 22% (moderately inflated)\n\nbenchConcatRoot :: Int -> Benchmark\nbenchConcatRoot i = bench (show i) $ xs `seq` nf concat xs\n  where\n    root = floor (sqrt (fromIntegral i)) :: Int\n    individualList = replicate root (0.0 :: Double)\n    totalList = replicate root individualList\n    xs = force totalList\n\n-- benchmarking concat/16\n-- time                 3.772 μs   (3.761 μs .. 3.786 μs)\n--                      1.000 R²   (1.000 R² .. 1.000 R²)\n-- mean                 3.809 μs   (3.789 μs .. 3.836 μs)\n-- std dev              83.20 ns   (65.32 ns .. 117.2 ns)\n-- variance introduced by outliers: 24% (moderately inflated)\n--\n-- benchmarking concat/32\n-- time                 8.904 μs   (8.557 μs .. 9.340 μs)\n--                      0.987 R²   (0.982 R² .. 0.992 R²)\n-- mean                 8.717 μs   (8.505 μs .. 9.043 μs)\n-- std dev              882.2 ns   (711.8 ns .. 1.230 μs)\n-- variance introduced by outliers: 87% (severely inflated)\n--\n-- benchmarking concat/64\n-- time                 16.73 μs   (16.57 μs .. 17.01 μs)\n--                      0.999 R²   (0.997 R² .. 1.000 R²)\n-- mean                 16.69 μs   (16.56 μs .. 16.83 μs)\n-- std dev              453.9 ns   (325.9 ns .. 670.8 ns)\n-- variance introduced by outliers: 29% (moderately inflated)\n\nbenchConcatManyLists :: Int -> Benchmark\nbenchConcatManyLists i = bench (show i) $ xs `seq` nf concat xs\n  where\n    individualList = replicate 25 (0.0 :: Double)\n    totalList = replicate i individualList\n    xs = force totalList\n\n-- benchmarking benchConcatLongLists/16\n-- time                 3.896 μs   (3.876 μs .. 3.917 μs)\n--                      1.000 R²   (0.999 R² .. 1.000 R²)\n-- mean                 3.893 μs   (3.877 μs .. 3.922 μs)\n-- std dev              69.18 ns   (48.06 ns .. 115.7 ns)\n-- variance introduced by outliers: 17% (moderately inflated)\n--\n-- benchmarking benchConcatLongLists/32\n-- time                 8.111 μs   (8.068 μs .. 8.150 μs)\n--                      1.000 R²   (0.999 R² .. 1.000 R²)\n-- mean                 8.106 μs   (8.068 μs .. 8.155 μs)\n-- std dev              140.6 ns   (112.6 ns .. 189.7 ns)\n-- variance introduced by outliers: 16% (moderately inflated)\n--\n-- benchmarking benchConcatLongLists/64\n-- time                 16.61 μs   (16.38 μs .. 16.92 μs)\n--                      0.999 R²   (0.997 R² .. 1.000 R²)\n-- mean                 16.50 μs   (16.39 μs .. 16.68 μs)\n-- std dev              461.9 ns   (304.2 ns .. 805.3 ns)\n-- variance introduced by outliers: 31% (moderately inflated)\nbenchConcatLongLists :: Int -> Benchmark\nbenchConcatLongLists i = bench (show i) $ xs `seq` nf concat xs\n  where\n    individualList = replicate i (0.0 :: Double)\n    totalList = replicate 25 individualList\n    xs = force totalList\n\nmain :: IO ()\nmain =\n  defaultMain\n    [ bgroup\n        \"benchConcatRoot\"\n        [benchConcatRoot (4 ^ i) | i <- [4, 5, 6]],\n      bgroup\n        \"benchConcatManyLists\"\n        [benchConcatManyLists (2 ^ i) | i <- [4, 5, 6]],\n      bgroup\n        \"benchConcatLongLists\"\n        [benchConcatLongLists (2 ^ i) | i <- [4, 5, 6]]\n    ]\n"
  },
  {
    "path": "hlint.yaml",
    "content": "# Haskell's Dangerous Functions\n# For a how-to-use and the latest version of this file go to:\n# https://github.com/NorfairKing/haskell-dangerous-functions/\n\n- ignore: { name: \"Use unless\" }\n- ignore: { name: \"Use tuple-section\" }\n\n- functions:\n  - {name: unsafeDupablePerformIO, within: []} # Unsafe\n  - {name: unsafeInterleaveIO, within: []} # Unsafe\n  - {name: unsafeFixIO, within: []} # Unsafe\n  - {name: unsafePerformIO, within: []} # Unsafe\n  - {name: unsafeCoerce, within: []} # Unsafe\n\n  # _VERY_ hard to get right, use the async library instead.\n  # See also https://github.com/informatikr/hedis/issues/165\n  - {name: forkIO, within: []}\n  # Mostly impossible to get right, rethink what you're doing entirely.\n  # See also https://www.reddit.com/r/haskell/comments/jsap9r/how_dangerous_is_forkprocess/\n  - {name: forkProcess, within: []}\n\n  - {name: undefined, within: []} # Purposely fails. Deal with errors appropriately instead.\n  - {name: throw, within: []} # Don't throw from pure code, use throwIO instead.\n  - {name: Prelude.error, within: []}\n\n  - {name: Data.List.head, within: []} # Partial, use `listToMaybe` instead.\n  - {name: Data.List.tail, within: []} # Partial\n  - {name: Data.List.init, within: []} # Partial\n  - {name: Data.List.last, within: []} # Partial\n  - {name: 'Data.List.!!', within: []} # Partial\n  - {name: Data.List.genericIndex, within: []} # Partial\n  - {name: Data.List.genericLength, within: []}\n\n  # Same, but for Data.Text\n  - {name: Data.Text.head, within: []}\n  - {name: Data.Text.tail, within: []}\n  - {name: Data.Text.init, within: []}\n  - {name: Data.Text.last, within: []}\n\n  - {name: minimum, within: []} # Partial\n  - {name: minimumBy, within: []} # Partial\n  - {name: maximum, within: []} # Partial\n  - {name: maximumBy, within: []} # Partial\n\n  # Same, but for Data.Text\n  - {name: Data.Text.maximum, within: []}\n  - {name: Data.Text.minimum, within: []}\n\n  - {name: GHC.Enum.pred, within: []} # Partial\n  - {name: GHC.Enum.succ, within: []} # Partial\n  - {name: GHC.Enum.toEnum, within: []} # Partial\n  - {name: GHC.Enum.fromEnum, within: []} # Does not do what you think it does.\n  - {name: GHC.Enum.enumFrom, within: []} # Does not do what you think it does, depending on the type.\n  - {name: GHC.Enum.enumFromThen, within: []} # Does not do what you think it does, depending on the type.\n  - {name: GHC.Enum.enumFromTo, within: []} # Does not do what you think it does, depending on the type.\n  - {name: GHC.Enum.enumFromThenTo, within: []} # Does not do what you think it does, depending on the type.\n\n  - {name: unless, within: []} # Really confusing, use 'when' instead.\n  - {name: either, within: []} # Really confusing, just use a case-match.\n\n  - {name: nub, within: []} # O(n^2)\n\n  - {name: Data.Foldable.foldl, within: []} # Lazy accumulator. Use foldl' instead.\n  - {name: Data.Foldable.foldMap, within: []} # Lazy accumulator. Use foldMap' instead.\n  - {name: Data.Foldable.sum, within: []} # Lazy accumulator\n  - {name: Data.Foldable.product, within: []} # Lazy accumulator\n\n  # Functions involving division\n  - {name: Prelude.quot, within: []} # Partial, see https://github.com/NorfairKing/haskell-WAT#num-int\n  - {name: Prelude.div, within: []}\n  - {name: Prelude.rem, within: []}\n  - {name: Prelude.mod, within: []}\n  - {name: Prelude.quotRem, within: []}\n  - {name: Prelude.divMod, within: []}\n\n  # Does unexpected things, see\n  # https://github.com/NorfairKing/haskell-WAT#real-double\n  - {name: realToFrac, within: []}\n\n  # Constructs rationals, which is either wrong or a bad idea.\n  - {name: 'Data.Ratio.%', within: []}\n\n  # Don't use string for command-line output.\n  - {name: System.IO.putChar, within: []}\n  - {name: System.IO.putStr, within: []}\n  - {name: System.IO.putStrLn, within: []}\n  - {name: System.IO.print, within: []}\n\n  # Don't use string for command-line input either.\n  - {name: System.IO.getChar, within: []}\n  - {name: System.IO.getLine, within: []}\n  - {name: System.IO.getContents, within: []} # Does lazy IO.\n  - {name: System.IO.interact, within: []}\n  - {name: System.IO.readIO, within: []}\n  - {name: System.IO.readLn, within: []}\n\n  # Don't use strings to interact with files\n  - {name: System.IO.readFile, within: []}\n  - {name: System.IO.writeFile, within: []}\n  - {name: System.IO.appendFile, within: []}\n\n  # Can succeed in dev, but fail in prod, because of encoding guessing\n  # It's also Lazy IO.\n  # See https://www.snoyman.com/blog/2016/12/beware-of-readfile/ for more info.\n  - {name: Data.Text.IO.readFile, within: []}\n  - {name: Data.Text.IO.Lazy.readFile, within: []}\n\n  - {name: Data.Text.Encoding.decodeUtf8, within: []} # Throws on invalid UTF8\n\n  - {name: fromJust, within: [], message: 'Partial'} # Partial\n\n  # Does silent truncation:\n  # > fromIntegral (300 :: Word) :: Word8\n  # 44\n  - {name: fromIntegral, within: []}\n  - {name: fromInteger, within: []}\n\n\n  - {name: 'read', within: []} # Partial, use `Text.Read.readMaybe` instead.\n  \n  # Deprecated, use `pure` instead.\n  # See https://gitlab.haskell.org/ghc/ghc/-/wikis/proposal/monad-of-no-return\n  - {name: 'return', within: []}\n\n- modules:\n  - { name: Control.Lens, within: [] }\n\n- extensions:\n  - { name: DeriveAnyClass, within: [] } # Dangerous\n\n  - { name: DuplicateRecordFields, within: [] }\n\n  - { name: NamedFieldPuns, within: [] }\n  - { name: TupleSections, within: [] }\n  - { name: OverloadedLabels, within: [] }\n"
  }
]