'let' best practices

This is probably a highly complicated question (possibly answered elsewhere), but I find myself wondering about it a lot – what are the main performance-improving pro tips for let expressions? I’m thinking of cases like the following:

update msg model =
    let
        newModel =
            complicatedTransformationOf model

        cmd =
            someFunctionOf newModel
    in
    ( newModel, cmd )

Is newModel calculated here twice? If it is, then is it more performant to write this instead?

update msg model =
    let
        f x =
            complicatedTransformationOf x

        cmd x =
            someFunctionOf x

        g x =
            (x, someFunctionOf x)
    in
    g (f model)
1 Like

I don’t know why you would think that newModel is computed twice. Is it because you’re thinking that values are lazy like in Haskell?

Anyway, no, newModel is only computed once. FYI, this is roughly what your code compiles to in JavaScript:

function update(msg, model) {
	var newModel = complicatedTransformationOf(model);
	var cmd = someFunctionOf(newModel);
	return _Utils_Tuple2(newModel, cmd);
});

newModel is only computed once, and then it’s value is referenced/used in two places.

Your second version is pretty much the same. If anything, it might be a tad slower because you have more function calls, but it’s to a negligible degree (unnoticeable if your function isn’t computed thousands of times per second).

I don’t think you generally need to concern yourself with performance around let expressions too much. They don’t have specific performance problems.

Your first version is just fine: it’s readable and you don’t have any performance penalty in there. Keep it the way it is.

3 Likes

I don’t know why you would think that newModel is computed twice.

Section 3.2 of The Implementation of Functional Languages by Peyton Jones seems to imply that you evaluate f newModel (using the let definition from the first example above) by calculating f (complicatedTransformationOf model). I never checked how Elm actually does it.

Thanks!

SPJ’s book is mostly about non-strict languages, whereas Elm is strict, so you can’t always apply lessons from the book to Elm.

Even if that section of the book is strictness agnostic, another aspect is that the book describes translating source code to an enhanced lambda calculus where there are no temporary variables. So even if there is such a translation step in Elm, with referential transparency, f model and newModel are interchangeable, and a good compiler ought to be able recognize repeated expression and optimize them away.

4 Likes