# Real World Haskell Revived

**URL:** <https://discourse.haskell.org/t/real-world-haskell-revived/14461>\
**Category:** Links\
**Created:** [July 26, 2026, 4:05pm UTC](https://discourse.haskell.org/t/real-world-haskell-revived/14461 "2026-07-26T16:05:35Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![jaror](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jaror/32/3271_2.png) [@jaror](https://discourse.haskell.org/u/jaror)\
**Post date:** [July 26, 2026, 4:05pm UTC](https://discourse.haskell.org/t/real-world-haskell-revived/14461/1 "2026-07-26T16:05:35Z")

</div>

Haskell is considered too theoretical by many, but Bryan, Don, and John, begged to differ by writing Real World Haskell. The book was written in collaboration with over 800 online commenters and released back to the public for free.

The book was such a success that it is still read by newcomers eighteen years later, but it is starting to show its age. So, I have now converted it to markdown and started modernizing it:

> **[real-world-haskell](https://codeberg.org/jaror/real-world-haskell)**
>
> An updated version of the Real World Haskell book.

---

<div class="post-metadata">

**Author:** ![bmobn00b](https://avatars.discourse-cdn.com/v4/letter/b/ecccb3/32.png) [@bmobn00b](https://discourse.haskell.org/u/bmobn00b)\
**Post date:** [July 26, 2026, 8:45pm UTC](https://discourse.haskell.org/t/real-world-haskell-revived/14461/2 "2026-07-26T20:45:43Z")

</div>

Great news, and there was another attempt: [GitHub - tssm/up-to-date-real-world-haskell: I'm trying to update the Real World Haskell book · GitHub](https://github.com/tssm/up-to-date-real-world-haskell)

---

<div class="post-metadata">

**Author:** ![jaror](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jaror/32/3271_2.png) [@jaror](https://discourse.haskell.org/u/jaror)\
**Post date:** [July 26, 2026, 8:56pm UTC](https://discourse.haskell.org/t/real-world-haskell-revived/14461/3 "2026-07-26T20:56:51Z")

</div>

Yes, I should have said that I am building on their work. I have now added it to the acknowledgments.

---

<div class="post-metadata">

**Author:** ![slow-dive](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/slow-dive/32/5585_2.png) [@slow-dive](https://discourse.haskell.org/u/slow-dive)\
**Post date:** [July 27, 2026, 8:19pm UTC](https://discourse.haskell.org/t/real-world-haskell-revived/14461/4 "2026-07-27T20:19:03Z")

</div>

Thank you for your work on this! I remember re-reading the monad transformers chapter a few times. Alas, for newcomers this new spot for the book might be a bit hard to find, but I’m not sure what to do about that.

I’ve gone through some of the chapters and I noticed that in chapter 3 the exercises at the end are enumerated 1-7, then after the code block they start with 1 again. I guess that’s a formatting error?

On another note, I couldn’t quite believe the statement in chapter 0 regarding `minima k xs = take k (sort xs)` being more performant due to laziness. I found [this](https://apfelmus.nfshost.com/articles/quicksearch.html) which explains the reasoning, but when I actually tried it, I saw no runtime difference between `sort [1..100000000]` and `take 3 $ sort [1..100000000]`.

---

<div class="post-metadata">

**Author:** ![Bodigrim](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/bodigrim/32/1457_2.png) [@Bodigrim](https://discourse.haskell.org/u/Bodigrim)\
**Post date:** [July 27, 2026, 8:39pm UTC](https://discourse.haskell.org/t/real-world-haskell-revived/14461/5 "2026-07-27T20:39:39Z")

</div>

> [@slow-dive](#):
>
> I saw no runtime difference between `sort [1..100000000]` and `take 3 $ sort [1..100000000]`.

How did you measure it?

---

<div class="post-metadata">

**Author:** ![jaror](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jaror/32/3271_2.png) [@jaror](https://discourse.haskell.org/u/jaror)\
**Post date:** [July 27, 2026, 8:44pm UTC](https://discourse.haskell.org/t/real-world-haskell-revived/14461/6 "2026-07-27T20:44:51Z")

</div>

> [@slow-dive](#):
>
> then after the code block they start with 1 again. I guess that’s a formatting error?

Thank you, [fixed](https://codeberg.org/jaror/real-world-haskell/commit/e056f132fb36780299fc226d3f5a403bc852f8ec)!

> [@slow-dive](#):
>
> I saw no runtime difference between `sort [1..100000000]` and `take 3 $ sort [1..100000000]`.

Strange, I also cannot see a significant speedup. Even when compiled with optimization using `tasty-bench`:

```haskell
{- cabal:
build-depends: base, tasty-bench
-}

import Test.Tasty.Bench
import Data.List (sort)

main = defaultMain
  [ bench "kminima" (whnf (take 3 . sort) xs)
  , bench "sort" (nf sort xs)
  ] where
  xs = [1..100000 :: Int]

```

GHC 9.10.3 by the way.

---

<div class="post-metadata">

**Author:** ![slow-dive](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/slow-dive/32/5585_2.png) [@slow-dive](https://discourse.haskell.org/u/slow-dive)\
**Post date:** [July 27, 2026, 8:46pm UTC](https://discourse.haskell.org/t/real-world-haskell-revived/14461/7 "2026-07-27T20:46:18Z")

</div>

I just ran `time ./Main` a few times so not in a very rigorous way I guess.

This was my attempt:

```haskell
module Main where

import System.Environment
import Data.List (sort)

runSortOnly :: IO ()
runSortOnly = do
    let !s = sort [1..100000000]
    return ()

runTakeSort :: IO ()
runTakeSort = do
    let !s = take 3 $ sort [1..100000000]
    return ()

main :: IO ()
main = do
    args <- getArgs
    case args of
        [] -> putStrLn "sort only" >> runSortOnly
        _ -> putStrLn "sort + take" >> runTakeSort

```

Both code paths take ~15s on my machine.

---

<div class="post-metadata">

**Author:** ![oisdk](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/oisdk/32/5283_2.png) [@oisdk](https://discourse.haskell.org/u/oisdk)\
**Post date:** [July 27, 2026, 9:10pm UTC](https://discourse.haskell.org/t/real-world-haskell-revived/14461/8 "2026-07-27T21:10:52Z")

</div>

Isn’t the problem that you’re using WHNF? I would think that that would computer the same thing in both cases, and you’d only see a difference with full nf

---

<div class="post-metadata">

**Author:** ![oisdk](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/oisdk/32/5283_2.png) [@oisdk](https://discourse.haskell.org/u/oisdk)\
**Post date:** [July 27, 2026, 9:14pm UTC](https://discourse.haskell.org/t/real-world-haskell-revived/14461/9 "2026-07-27T21:14:30Z")

</div>

Possibly `[1..1000000]` is a case where `sort` is almost linear anyway? Maybe if you try it with the same list in reverse, or random numbers (I know that `sort` does a lot of work to try and detect runs)

---

<div class="post-metadata">

**Author:** ![jaror](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jaror/32/3271_2.png) [@jaror](https://discourse.haskell.org/u/jaror)\
**Post date:** [July 27, 2026, 9:16pm UTC](https://discourse.haskell.org/t/real-world-haskell-revived/14461/10 "2026-07-27T21:16:40Z")

</div>

That’s it! Reversing didn’t work, but pseudo random numbers do show the difference:

```haskell
{- cabal:
build-depends: base, tasty-bench
-}

import Test.Tasty.Bench
import Data.List (sort)

lcg :: Int -> Int
lcg n = n * 313 + 1 `mod` 1000

main = defaultMain
  [ bench "kminima" (whnf (take 3 . sort) xs)
  , bench "sort" (nf sort xs)
  ] where
  xs = take 100000 (iterate lcg 0)

```

```screen
All
  kminima: OK
    4.32 ms ± 316 μs, 10 MB allocated, 7.1 MB copied, 25 MB peak memory
  sort: OK
    62.2 ms ± 2.2 ms, 112 MB allocated, 43 MB copied, 25 MB peak memory

```

---

<div class="post-metadata">

**Author:** ![slow-dive](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/slow-dive/32/5585_2.png) [@slow-dive](https://discourse.haskell.org/u/slow-dive)\
**Post date:** [July 28, 2026, 10:51am UTC](https://discourse.haskell.org/t/real-world-haskell-revived/14461/11 "2026-07-28T10:51:52Z")

</div>

I am trying to grasp this comment. I guess I just don’t have an intuitive understanding of this topic yet ☹

I think I am close to understanding it now that I tried this in ghci:

```haskell
ghci> xs <- getRandomList
ghci> !a = sort xs
ghci> !b = take 3 $ sort xs
ghci> :sprint a
a = 2 : _
ghci> :sprint b
b = 2 : _

```

I had thought `let !s = ...` would completely evaluate s, but I guess it only evaluates the head of the list (i.e. the list data constructor is in WHNF? I am not completely sure where I am “using WHNF”)?

Is this correct? How could I easily “use NF” then?

---

<div class="post-metadata">

**Author:** ![jaror](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jaror/32/3271_2.png) [@jaror](https://discourse.haskell.org/u/jaror)\
**Post date:** [July 28, 2026, 11:05am UTC](https://discourse.haskell.org/t/real-world-haskell-revived/14461/12 "2026-07-28T11:05:40Z")

</div>

Yes, as you can see with the `:sprint` command in GHCi, using bang patterns (the `!` syntax) only evaluates the first constructor of the list.

To evaluate the list completely you have to produce a value that depends on all of the list constructors. You can do that by traversing the whole list and produce a dummy result (in the unit type):

```haskell
ghci> go [] = (); go (_:xs) = go xs
ghci> go $ sort xs
()

```

You can also define that using `foldr`:

```haskell
ghci> foldr (\_ y -> y) () $ sort xs

```

Also note that the elements of the list might still not be evaluated. You can use the `seq` function to evaluate that to WHNF.

```haskell
ghci> foldr seq () $ sort xs

```

---

<div class="post-metadata">

**Author:** ![jaror](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jaror/32/3271_2.png) [@jaror](https://discourse.haskell.org/u/jaror)\
**Post date:** [July 28, 2026, 11:07am UTC](https://discourse.haskell.org/t/real-world-haskell-revived/14461/13 "2026-07-28T11:07:42Z")

</div>

I would recommend these tutorials for more info:

> **[apfelmus - The Incomplete Guide to Lazy Evaluation (in Haskell)](https://apfelmus.nfshost.com/articles/lazy-eval.html)**
>
> Lazy evaluation is the most widely used method for executing Haskell program code on a computer. This series of tutorials provides a thorough introduction to this topic.

I wonder if something like that could be included in the modernized real world book.

---

<div class="post-metadata">

**Author:** ![sclv](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/sclv/32/71_2.png) [@sclv](https://discourse.haskell.org/u/sclv)\
**Post date:** [July 28, 2026, 8:21pm UTC](https://discourse.haskell.org/t/real-world-haskell-revived/14461/14 "2026-07-28T20:21:02Z")

</div>

> [@slow-dive](#):
>
> Alas, for newcomers this new spot for the book might be a bit hard to find, but I’m not sure what to do about that.

Once this project is closer to complete it could be added to the [haskell.org](http://haskell.org) homepage as a resource.

---

<div class="post-metadata">

**Author:** ![exaexa](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/exaexa/32/4753_2.png) [@exaexa](https://discourse.haskell.org/u/exaexa)\
**Post date:** [July 29, 2026, 4:45am UTC](https://discourse.haskell.org/t/real-world-haskell-revived/14461/15 "2026-07-29T04:45:35Z")

</div>

Hi! Thanks for starting this. Is there any plan for larger extensions of the original contents? I thought I’d submit a chapter or two.

---

<div class="post-metadata">

**Author:** ![jaror](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jaror/32/3271_2.png) [@jaror](https://discourse.haskell.org/u/jaror)\
**Post date:** [July 29, 2026, 8:27am UTC](https://discourse.haskell.org/t/real-world-haskell-revived/14461/16 "2026-07-29T08:27:47Z")

</div>

As long as they fit with the theme of teaching Haskell through practical examples, I’m all for additional chapters. I also wouldn’t want beginners to find the length of the book daunting, but maybe we can split it off into multiple volumes if that becomes a real concern.

---

<div class="post-metadata">

**Author:** ![artem](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/artem/32/56_2.png) [@artem](https://discourse.haskell.org/u/artem)\
**Post date:** [July 30, 2026, 4:54am UTC](https://discourse.haskell.org/t/real-world-haskell-revived/14461/17 "2026-07-30T04:54:23Z")

</div>

Do you have a plan for deployment as a website, or do you intend the readers to use the Codeberg file viewer?

---

<div class="post-metadata">

**Author:** ![exaexa](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/exaexa/32/4753_2.png) [@exaexa](https://discourse.haskell.org/u/exaexa)\
**Post date:** [July 30, 2026, 6:23am UTC](https://discourse.haskell.org/t/real-world-haskell-revived/14461/18 "2026-07-30T06:23:54Z")

</div>

I guess that’s the goal. Hosting shouldn’t be hard to find, for a bare minimum Codeberg has some pages support now. I can provide some hosting VMs too.

---

<div class="post-metadata">

**Author:** ![jaror](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/jaror/32/3271_2.png) [@jaror](https://discourse.haskell.org/u/jaror)\
**Post date:** [July 30, 2026, 11:44am UTC](https://discourse.haskell.org/t/real-world-haskell-revived/14461/19 "2026-07-30T11:44:39Z")

</div>

I think perhaps the best way to use the book is to clone the repository and read it in VSCodium with the extensions mentioned in the contributing section in the readme. That way you have easy access to the code examples and they can also easily write their own exercise solutions or open a ghci repl. I have added a todo to write up a little tutorial for setting that up.

A static website would still be nice to use as a reference or on mobile devices. I’m considering [https://www.mkdocs.org/](https://www.mkdocs.org/) for that purpose, but this is outside my area of expertise so I am open for suggestions. Thanks @exaexa for offering to help with hosting. I will probably first try Codeberg pages and see how well that works.

---

<div class="post-metadata">

**Author:** ![dawkrish](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/dawkrish/32/4746_2.png) [@dawkrish](https://discourse.haskell.org/u/dawkrish)\
**Post date:** [July 30, 2026, 10:02pm UTC](https://discourse.haskell.org/t/real-world-haskell-revived/14461/20 "2026-07-30T22:02:14Z")

</div>

Is there a roadmap or a timeline?

[Next page](https://discourse.haskell.org/t/real-world-haskell-revived/14461.md?page=2)
