Lazy evaluated map

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?

1 Like

I thought for certain that’d be in elm-review-simplify 2.0.7 but I don’t see it listed. At the very least I think I’ve seen @jfmengels mention it in passing.

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

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

may be more readable than

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

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

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

         else
            identity
        )

would become

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

                else
                    identity
               )
        )

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


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 :clap: ).

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.

3 Likes

It’s also fairly straightforward to write a library that does this in user land.

Transducers seems to be quite similar to iterators like Iter - elm-iter 1.0.0 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 …

2 Likes

Fundamentally there seems to be more than one way to skin a cat.

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).

2 Likes

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

3 Likes

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