# Lazy evaluated map

**URL:** <https://discourse.elm-lang.org/t/lazy-evaluated-map/7947>\
**Category:** Learn\
**Created:** [November 29, 2021, 7:39pm UTC](https://discourse.elm-lang.org/t/lazy-evaluated-map/7947 "2021-11-29T19:39:42Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Lucas\_Payr](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/lucas_payr/32/4993_2.png) [@Lucas\_Payr](https://discourse.elm-lang.org/u/Lucas_Payr)\
**Post date:** [November 29, 2021, 7:39pm UTC](https://discourse.elm-lang.org/t/lazy-evaluated-map/7947/1 "2021-11-29T19:39:42Z")

</div>

My question is simple:

Why does Elm not rewrite `List.map f >> List.map g` into `List.map (f >> g)`?

Because the thing is, this optimization can be seen in Java streams, (which is neither a lazy nor a pure functional language) yet it does not happen in Elm. Why is that?

---

<div class="post-metadata">

**Author:** ![wolfadex](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/wolfadex/32/2461_2.png) [@wolfadex](https://discourse.elm-lang.org/u/wolfadex)\
**Post date:** [November 30, 2021, 1:21am UTC](https://discourse.elm-lang.org/t/lazy-evaluated-map/7947/2 "2021-11-30T01:21:48Z")

</div>

I thought for certain that’d be in [elm-review-simplify 2.0.7](https://elm.dmy.fr/packages/jfmengels/elm-review-simplify/latest) but I don’t see it listed. At the very least I think I’ve seen @jfmengels mention it in passing.

---

<div class="post-metadata">

**Author:** ![jfmengels](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/jfmengels/32/3124_2.png) [@jfmengels](https://discourse.elm-lang.org/u/jfmengels)\
**Post date:** [November 30, 2021, 9:20am UTC](https://discourse.elm-lang.org/t/lazy-evaluated-map/7947/3 "2021-11-30T09:20:18Z")

</div>

> [@wolfadex](#):
>
> I thought for certain that’d be in [elm-review-simplify 2.0.7](https://elm.dmy.fr/packages/jfmengels/elm-review-simplify/latest)

It’s not part of `elm-review-simplify` because this is not a simplification 100% of the time in terms of readability.

```elm
a
  |> List.map toX
  |> List.map toY

```

may be more readable than

```elm
a
  |> List.map (toX >> toY)

```

especially for people who dislike or have trouble reading `>>`. Also, in some cases, things get pretty

```elm
a
    |> List.map toX
    |> List.map
        (if condition then
            toY

         else
            identity
        )

```

would become

```elm
a
    |> List.map
        (toX
            >> (if condition then
                    toY

                else
                    identity
               )
        )

```

which I believe is worse (not that the former was _that_ much better though).

* * *

> [@Lucas\_Payr](#):
>
> Why does Elm not rewrite `List.map f >> List.map g` into `List.map (f >> g)` ?

The Elm compiler actually does very little work transforming the code. As far as I can tell, it transforms things like `+`into real additions (instead of function calls) and `|>` into regular function calls, and that’s the extent to which the compiler optimizes things (and it’s already really fast 👏 ).

I am currently looking into merging map functions and the sorts together through `elm-optimize-level-2`. If it works out well, then hopefully it will make it into the compiler at one point. From preliminary benchmarks, it does look like it improves performance.

That said, there is a problem with optimizing Elm core code like this, which is that custom code won’t receive the same optimizations. Say you wrote a thin wrapper around List named `MyList` to enforce some constraints. If the compiler were to optimize `a |> List.map f |> List.map g`, it wouldn’t do the same thing for `a |> MyList.map f |> MyList.map g`, because the compiler doesn’t know how this `map` function works.

And the same thing applies for other kinds of optimizations. If you want to benefit from the same optimizations because `MyList` is used in performance-critical code, you’d need to remove the wrapper and start using `List`, which is not an incentive for good and maintainable code.

It’s like having regular-speed car lanes on a highway for almost everything in Elm, and then a fast lane for core custom types. Once you have a fast lane, the other lanes become slow lanes.

Does this mean we shouldn’t apply this transformation? Not necessarily, but it’s something to keep in mind. I believe we should try and find ways to make all lanes fast, but that will probably be hard.

---

<div class="post-metadata">

**Author:** ![gampleman](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/gampleman/32/43_2.png) [@gampleman](https://discourse.elm-lang.org/u/gampleman)\
**Post date:** [November 30, 2021, 10:05am UTC](https://discourse.elm-lang.org/t/lazy-evaluated-map/7947/4 "2021-11-30T10:05:55Z")

</div>

It’s also fairly straightforward to [write a library that does this in user land](https://package.elm-lang.org/packages/avh4-experimental/elm-transducers/latest/).

---

<div class="post-metadata">

**Author:** ![mattpiz](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/mattpiz/32/860_2.png) [@mattpiz](https://discourse.elm-lang.org/u/mattpiz)\
**Post date:** [November 30, 2021, 2:32pm UTC](https://discourse.elm-lang.org/t/lazy-evaluated-map/7947/5 "2021-11-30T14:32:43Z")

</div>

Transducers seems to be quite similar to iterators like [Iter - elm-iter 1.0.0](https://package.elm-lang.org/packages/fabiommendes/elm-iter/latest/Iter) for the purpose of computing multiple transformations before reducing. Do you know of good resources explaining the fundamental differences? I feel like transducers are more powerful since I remember reading about those when I was looking for operations that could mutate data in place in a type-safe way but I haven’t look at those things for a long time …

---

<div class="post-metadata">

**Author:** ![gampleman](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/gampleman/32/43_2.png) [@gampleman](https://discourse.elm-lang.org/u/gampleman)\
**Post date:** [November 30, 2021, 3:56pm UTC](https://discourse.elm-lang.org/t/lazy-evaluated-map/7947/6 "2021-11-30T15:56:32Z")

</div>

Fundamentally there [seems to be more than one way to skin a cat](https://package.elm-lang.org/packages/the-sett/lazy-list/latest/Seq).

In the broader view you’re gonna go somewhere between the simplicity of the for loop and the efficiency of SQL depending on your needs in terms of optimisation, composition and predictability.

I think the cool thing about transducers is that they provide a very cool interface for the `fromList`/`toList` stuff that is entirely abstracted from the datastrucutre (as long as it has some neccesary operations).

---

<div class="post-metadata">

**Author:** ![lpil](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/lpil/32/3139_2.png) [@lpil](https://discourse.elm-lang.org/u/lpil)\
**Post date:** [December 1, 2021, 6:49pm UTC](https://discourse.elm-lang.org/t/lazy-evaluated-map/7947/7 "2021-12-01T18:49:51Z")

</div>

> [@Lucas\_Payr](#):
>
> Because the thing is, this optimization can be seen in Java streams, (which is neither a lazy nor a pure functional language) yet it does not happen in Elm. Why is that?

In Java it’s not a compile level optimisation- it’s just normal Java code that is written to be lazy. This can be done in Elm just the same, as in the libraries linked by @gampleman and @mattpiz

---

<div class="post-metadata">

**Author:** ![system](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/1X/50a05e53677a2c3b47776d7abd0f113eb50193a1.png) [@system](https://discourse.elm-lang.org/u/system)\
**Post date:** [December 11, 2021, 6:50pm UTC](https://discourse.elm-lang.org/t/lazy-evaluated-map/7947/8 "2021-12-11T18:50:47Z")

</div>

Closed on December 11, 2021, 6:50pm UTC

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
