Require calling lazy
exclusively at the top level of a point free function with a lambda expression as its first argument.
rule : Review.Rule.Rule
This is a highly opinionated rule that requires all calls of lazy
to be at the top level of a point-free function with a lambda expression as the first argument.
This prevents errors in the view function argument to lazy.
view : Model -> Html a
view = lazy (\model -> ...)
view2 : Model -> Time.Zone -> Html a
view2 = lazy (\model timezone -> ...)
Proper use of lazy
is extremely hard to get right and on top of that maintaining correctness in the presence of refactorings is even worse!
In order for lazy
to be able to skip evaluating the view function (and thus avoiding a potentionally expensive computation)
the Elm runtime will check each argument to lazy
against the value from the previous call using Javascript reference equality (think ===
). Note
that this includes the first function argument to lazy as well!
A very common source of errors is to not have a top-level function as the first arugment to an unmemoized lazy call, but instead having a partially applied function or a function defined in a let or a function composition or a lambda expression.
For example all of the following functions are bad and will result in lazy calls never being cached:
badView1 : Model -> Int -> Html a
badView1 model counter =
lazy (viewFunc counter) model
badView2 : Model -> Html a
badView2 model =
lazy (\m -> viewFunc model) model
badView3 : Model -> Html a
badView3 model =
let viewFunc m = ...
in
lazy viewFunc model
However, if we define our view to be point-free, then our application of the view function to lazy is effectively memoized, and the paritially applied and lambda styles above are permissable.
For example:
goodView1 : Model -> Html a
goodView1 =
lazy (viewFunc x)
goodView2 : Model -> Html a
goodView2 =
lazy (\model -> viewFunc model)
As this is an opinionated rule, we choose to enforce the lambda expression form for several reasons:
func = \x1 x2 -> x1 + x2
). Some functional languages (like Roc) even require functions to be defined in this form.This rule also adds a couple additional contraints that aren't stricly necessary, but we think are helpful for clarity:
lazy
function. lazy
requires a labmda expression accepting 1 argument, lazy2
requires a lambda with 2 arguments and so on.This rule applies to all calls of lazy
... lazy8
from Html.Lazy
and lazy
... lazy7
from Html.Styled.Lazy