# NoEtaReducibleLambdas elm-review rule

**URL:** <https://discourse.elm-lang.org/t/noetareduciblelambdas-elm-review-rule/8206>\
**Category:** Show and Tell\
**Created:** [February 27, 2022, 4:32pm UTC](https://discourse.elm-lang.org/t/noetareduciblelambdas-elm-review-rule/8206 "2022-02-27T16:32:30Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![john\_s](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/john_s/32/4425_2.png) [@john\_s](https://discourse.elm-lang.org/u/john_s)\
**Post date:** [February 27, 2022, 4:32pm UTC](https://discourse.elm-lang.org/t/noetareduciblelambdas-elm-review-rule/8206/1 "2022-02-27T16:32:30Z")

</div>

I published a rule that can be used to enforce varying levels of function simplification  
[elm-review-reducible-lambdas](https://package.elm-lang.org/packages/jsuder-xx/elm-review-reducible-lambdas/latest/). The default settings are probably a little more aggressive than most in the community would prefer. However, you may be able to tinker with the settings to find a useful sweet spot for your project and so I decided to share in case anyone finds this useful.

## Conservative Settings

This example config will only reduce single argument lambdas that have single letter argument names (thanks to @Arkham for the argument name predicate suggestion).

**Error**

- `\a -> someFunction a` will reduce to `someFunction`

**Not Error**

- `\apples -> someFunction apples` due to argument name length
- `\a b -> someFunction a b` due to multiple arguments.

```elm
config : List Rule
config =
    [ NoEtaReducibleLambdas.rule
        { lambdaReduceStrategy = NoEtaReducibleLambdas.OnlyWhenSingleArgument
        , argumentNamePredicate = \argumentName -> String.length argumentName == 1
        }
    ]

```

## More Aggressive

These are the default settings. These may reduce a little too aggressively for most.

**Error**

- `\apples -> someFunction apples` reduces to `someFunction`
- `\apples bananas -> someFunction apples bananas` reduces to `someFunction`
- `\apples bananas -> someFunction milk apples bananas` reduces to `someFunction milk`
- `\apples bananas -> fn (apples 15) bananas` reduces to `\apples -> fn (apples 15)`

**Not Error**

- `\bananas -> fn (apples 15) bananas` because removing the lambda would change performance attributes by immediately evaluating `(apples 15)`.

```elm
config : List Rule
config =
    [ NoEtaReducibleLambdas.rule
        { lambdaReduceStrategy = NoEtaReducibleLambdas.RemoveLambdaWhenNoCallsInApplication
        , argumentNamePredicate = always True
        }
    ]

```

---

<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:** [February 27, 2022, 9:53pm UTC](https://discourse.elm-lang.org/t/noetareduciblelambdas-elm-review-rule/8206/2 "2022-02-27T21:53:55Z")

</div>

Hey @john_s!

Very nice work! I know quite a few people are interested in this because I’ve seen asked a number of times whether this existed. Well now it does! 🙌

You mention changing performance attributes, so I just want to mention that even the changes that this rule makes can have some performance impacts.

When they have multiple arguments, Elm functions are wrapped in a FX function (F2, F3, F4, …), and when functions are called with multiple argument, they are called with a AX function (A2, A3, A4, …). What the AX functions do is check whether the function to call is wrapped in a FX function of the same number (A2 and F2, A3 and F3, …). If it’s the case, then the function is called with the original functions like `f(a, b, c, ...)`. If it isn’t, then the function will be called one argument at a time `f(a)(b)(c)...` until all the arguments are passed, which is noticeably slower for a function that is often called.

@robin.heggelund wrote a very nice series of articles on Elm performance, including one article explaining [How Elm functions work](https://blogg.bekk.no/how-elm-functions-work-71cab7426a2f), and its following articles explain how they got faster (links to those at the end of [this article](https://medium.com/bekk/successes-and-failures-in-optimizing-elms-runtime-performance-c8dc88f4e623)).

That means that `List.map (\apples bananas -> someFunction milk apples bananas) list` will be faster than `List.map (someFunction milk) list`. Of course, always benchmark instead of making a claim, so I wrote a [benchmark](https://github.com/jfmengels/elm-benchmarks/blob/master/src/WhatIsFaster/CallingCurriedFunctions.elm).

For Elm 0.19.1, performance is faster with the curried version (I was very surprised to be honest, and I don’t know how to explain) by 5%-10%. However, when run with `elm-optimize-level-2`, the curried version is 3.5 times slower (according to this particular benchmark at least).

Of course, this rule can also have the opposite effect. when you have a function defined as `someFunction a = \b -> ...`, then having it called like `List.map (someFunction 1) list` should be more performant than `List.map (\a -> someFunction 1 a) list`. In this case, the rule would push towards the former version which would be a good thing.

These performance impacts is the reason why it didn’t explore this rule further. But I think that there are some cases where it’s always going to be okay or better (on a performance level, not in terms of personal preferences), like for `\apples -> someFunction apples` or `\apples bananas -> someFunction apples bananas` being reduced to `someFunction`.

I think I would see the value in having a setting for the rule that only reports errors when the performance can be estimated to be at least as good as the current version. I see at least 2 scenarios when it could do that:

1. When the entire lambda can be reduced to a single variable name, like `\apples -> someFunction apples` → `soimeFunction` (that said, this should be benchmarked)
2. When the rule knows which wrapper the function uses. So if we have `someFunction milk = \apples bananas -> ...`, then we can simplify `\apples bananas -> someFunction milk apples bananas` to `someFunction milk` (and otherwise this should be left untouched). This information can be gathered by looking at the definitions of each function. When the information is missing (code from dependencies, or functions from arguments), then no error should be reported.

What do you think?

PS: What does “Eta” mean?

---

<div class="post-metadata">

**Author:** ![robin.heggelund](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/robin.heggelund/32/1330_2.png) [@robin.heggelund](https://discourse.elm-lang.org/u/robin.heggelund)\
**Post date:** [February 28, 2022, 2:32am UTC](https://discourse.elm-lang.org/t/noetareduciblelambdas-elm-review-rule/8206/3 "2022-02-28T02:32:49Z")

</div>

The reason `List.map (someFunction apple) bananas` Is faster is because the curried call returns a single-arity function which will be executed directly by List.map, while `List.map (\apples banana -> someFunction apples banana) bananas` will have to create a curried version of the lambda (with F2, which has some overhead) and then List.map essentially calls two functions when executing (first the lambda through A2, then someFunction through A2).

---

<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:** [March 1, 2022, 10:51am UTC](https://discourse.elm-lang.org/t/noetareduciblelambdas-elm-review-rule/8206/4 "2022-03-01T10:51:37Z")

</div>

_η-reduction_ (η pronounced “eta”) is the transformation of `\x -> f x` to `f`, conversely _η-abstraction_ or _η-expansion_ is the reverse transformation.

[https://wiki.haskell.org/Eta\_conversion](https://wiki.haskell.org/Eta_conversion)

---

<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:** [March 11, 2022, 10:51am UTC](https://discourse.elm-lang.org/t/noetareduciblelambdas-elm-review-rule/8206/5 "2022-03-11T10:51:54Z")

</div>

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