# Strict, StrictData, UNPACK

**URL:** <https://discourse.haskell.org/t/strict-strictdata-unpack/9151>\
**Category:** Uncategorized\
**Created:** [March 24, 2024, 10:26am UTC](https://discourse.haskell.org/t/strict-strictdata-unpack/9151 "2024-03-24T10:26:04Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![tomjaguarpaw](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/tomjaguarpaw/32/1230_2.png) [@tomjaguarpaw](https://discourse.haskell.org/u/tomjaguarpaw)\
**Post date:** [March 24, 2024, 10:26am UTC](https://discourse.haskell.org/t/strict-strictdata-unpack/9151/1 "2024-03-24T10:26:04Z")

</div>

Forking off a thread from @vshabanov’s comment [One Billion Row challenge in Hs - #223 by vshabanov](http://discourse.haskell.org/t/one-billion-row-challenge-in-hs/8946/223)

* * *

> [@One Billion Row challenge in Hs](http://discourse.haskell.org/t/one-billion-row-challenge-in-hs/8946/223):
>
> `-XStrictData` might be useful as a quick check, but I think it’s better to have explicit bangs in record fields.

I don’t feel too strongly. Less experienced users might be better off with `-XStrictData` but then there’s also the risk they then don’t learn to use `!`.

> [@One Billion Row challenge in Hs](http://discourse.haskell.org/t/one-billion-row-challenge-in-hs/8946/223):
>
> BTW, a related performance pitfall is to use `Record { field :: {-# UNPACK #-} !Int }` and then send the `field` to a non-inlined function that expects a boxed value. This will cause allocation (boxing) and may actually reduce performance.

Yes, and this is explained further in the [User’s Guide](https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/pragmas.html#unpack-pragma). But firstly, I don’t think there’s much risk of `UNPACK` happening by accident, and secondly unboxing is not _that_ closely related to laziness. There are morally two steps between something unboxed and something lazy: 1. unboxing, 2. lifting.

> [@One Billion Row challenge in Hs](http://discourse.haskell.org/t/one-billion-row-challenge-in-hs/8946/223):
>
> Everything is lazy is usually faster … Making everything strict kills a lot of expressiveness and, counter-intuitively, usually makes things slower.

I don’t understand this. I expect that making fields of data types strict to be better for performance in the vast majority of cases. There are exceptions when you can avoid a redundant calculation by returning a value in a lazy field, but I expect such cases to not arise much in performance critical code anyway.

Do you have some examples you can share of strict fields making performance worse (where the worsening cannot be attributed to redundant computation)?

> [@One Billion Row challenge in Hs](http://discourse.haskell.org/t/one-billion-row-challenge-in-hs/8946/223):
>
> For the “Haskell must be strict” folk

Who are they? For the record, I am not one. I think lazy function arguments are wonderful. My current expectation, though, is that strict fields of data types are the correct default choice in the vast majority of cases.

> [@One Billion Row challenge in Hs](http://discourse.haskell.org/t/one-billion-row-challenge-in-hs/8946/223):
>
> see [a take from Edward Kmett](https://www.reddit.com/r/haskell/comments/l98v73/comment/glgun65/) on how laziness is necessary for performance in a pure language (basically a short description of the chapter “Amortization and persistence via lazy evaluation” from Okasaki’s book).

Despite valuing lazy function arguments, I haven’t been able to understand Ed’s insistence that what he wants can’t be achieved in a strict language with explicit laziness. Okasaki’s book was written in such a language, after all.

---

<div class="post-metadata">

**Author:** ![atravers](https://avatars.discourse-cdn.com/v4/letter/a/45deac/32.png) [@atravers](https://discourse.haskell.org/u/atravers)\
**Post date:** [March 24, 2024, 1:32pm UTC](https://discourse.haskell.org/t/strict-strictdata-unpack/9151/2 "2024-03-24T13:32:55Z")

</div>

> [@tomjaguarpaw](#):
>
> I haven’t been able to understand Ed’s insistence that what he wants can’t be achieved in a strict language with explicit laziness.

- From page 26 of 33 in [How to Declare an Imperative](https://ics.uci.edu/~jajones/INF102-S18/readings/24_wadler) (1997)

- From [More points for lazy evaluation](http://augustss.blogspot.com/2011/05/more-points-for-lazy-evaluation-in.html) (2011)

---

<div class="post-metadata">

**Author:** ![vshabanov](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.haskell.org/vshabanov/32/2080_2.png) [@vshabanov](https://discourse.haskell.org/u/vshabanov)\
**Post date:** [March 25, 2024, 12:10am UTC](https://discourse.haskell.org/t/strict-strictdata-unpack/9151/3 "2024-03-25T00:10:56Z")

</div>

> [@tomjaguarpaw](#):
>
> I don’t feel too strongly. Less experienced users might be better off with `-XStrictData` but then there’s also the risk they then don’t learn to use `!`.

I regularly have to dig into a large Haskell/Mu codebase. Seeing a data declaration and not knowing that it’s strict can be quite misleading. In general, having a LANGUAGE pragma somewhere invisibly affecting the code is not good for maintenance (but good for quick experimentation).

> [@tomjaguarpaw](#):
>
> Yes, and this is explained further in the [User’s Guide](https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/pragmas.html#unpack-pragma).

It explains that you need `-O`. But reboxing can happen with `-O2`. Not every function can be inlined. I’ve seen increases in allocations due to UNPACK.

> [@tomjaguarpaw](#):
>
> But firstly, I don’t think there’s much risk of `UNPACK` happening by accident, and secondly unboxing is not _that_ closely related to laziness.

I added it because it looks like a similar silver bullet that makes code more performant, but it doesn’t always help.

> [@tomjaguarpaw](#):
>
> I don’t understand this. I expect that making fields of data types strict to be better for performance in the vast majority of cases. There are exceptions when you can avoid a redundant calculation by returning a value in a lazy field, but I expect such cases to not arise much in performance critical code anyway.

How much of the code is performance critical? I would say that for an average webapp, or maybe even a compiler, the majority of the code is not performance critical. And I would argue that redundant computation is the norm rather than the exception.

> [@tomjaguarpaw](#):
>
> Do you have some examples you can share of strict fields making performance worse (where the worsening cannot be attributed to redundant computation)?

No (except when paired with `-funbox-strict-fields`), I’m mostly pointing that redundant computations are pretty frequent.

Another pitfall is using non-strict data as strict fields. `field :: !(Maybe foo)` won’t help much. One needs to use strict data all the way down, which is not that convenient.

> [@tomjaguarpaw](#):
>
> Who are they?

I’ve seen several messages about strictness and performance in the original thread, and warned that it’s not as simple as making everything strict.

> [@tomjaguarpaw](#):
>
> My current expectation, though, is that strict fields of data types are the correct default choice in the vast majority of cases.

I think there is a single rule of thumb: “is it an accumulator? make it strict”.

Other cases are more nuanced. If it’s an aggregation – the result data is smaller than the source data AND we don’t want to keep the source data AND we are fine with always doing the aggregation – ok, either force evaluation or maybe make it strict.

If the data is created once and not updated recursively, the need for StrictData is not as strict, and the gain from not evaluating is greater.

YMMV, if you are dealing with numeric code, or have a codebase that is prone to space leaks for some reason, then you may need to enforce StrictData (and use StrictMaybe, StrictEither, etc). But this can make ergonomics worse.

There are alternative approaches like spine-strict data, `deepseq` (inefficient “traverse it twice” hammer, but can work when there’s no time to find a leak), `seq` at the point of creating a big thunk that is then put to a lazy field. Lots of them.

> [@tomjaguarpaw](#):
>
> Despite valuing lazy function arguments, I haven’t been able to understand Ed’s insistence that what he wants can’t be achieved in a strict language with explicit laziness. Okasaki’s book was written in such a language, after all.

Good point. I have been programming in OCaml for several years. It’s strict and has lazy values, and I would say that programming in a lazy-by-default language feels much more pleasant (not sure how a lazy language with strict data would feel, though).

Ed’s take includes the modularity part as well. The performance part can be reproduced in a strict language (OCaml is faster than Haskell in many cases), but modularity (with performance) is more challenging to achieve.
