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.
What does this look like?
view : Model -> Html a
view = lazy (\model -> ...)
view2 : Model -> Time.Zone -> Html a
view2 = lazy (\model timezone -> ...)
Motivation
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
A Better Path
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:
It forces the remaining lazy arguments to have explicitly defined local names, which improves readability as the point-free requirement means they are not named in the function definition itself.
The code for the entire view can remain in one function, if the author so wishes.
It just plain looks like a standard function definition (e.g. func = \x1 x2 -> x1 + x2). Some functional languages (like Roc) even require functions to be defined in this form.
Minutiae
This rule also adds a couple additional contraints that aren't stricly necessary, but we think are helpful for clarity:
Functions calling lazy must have a type signature (which assists the other rules in this package).
The lambda expression argument must be of full airity for the given lazy function. lazy requires a labmda expression accepting 1 argument, lazy2 requires a lambda with 2 arguments and so on.
Additional Notes
This rule applies to all calls of lazy ... lazy8 from Html.Lazy
and lazy ... lazy7 from Html.Styled.Lazy
This is a highly opinionated rule that requires all calls of
lazyto 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.What does this look like?
Motivation
Proper use of
lazyis extremely hard to get right and on top of that maintaining correctness in the presence of refactorings is even worse!In order for
lazyto be able to skip evaluating the view function (and thus avoiding a potentionally expensive computation) the Elm runtime will check each argument tolazyagainst 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:
A Better Path
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:
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.Minutiae
This rule also adds a couple additional contraints that aren't stricly necessary, but we think are helpful for clarity:
lazyfunction.lazyrequires a labmda expression accepting 1 argument,lazy2requires a lambda with 2 arguments and so on.Additional Notes
This rule applies to all calls of
lazy...lazy8from Html.Lazy andlazy...lazy7from Html.Styled.Lazy