Scoping construct for dependent constants

I sometimes want to write a function which needs to apply a bunch of transformations that are not functions of a single parameter only:

f[x_] := h[g3[g2[g1[x, 37], Some -> Stuff], "foo"], "bar"];

I find this hard to read code. And I would like to give names to the intermediate results.

If these were single parameter functions one could just write f = h @* g3 @* g2 @* g1 but that does not work in this case. One could try to curry away the additional parameters, but sometimes the some other intermediate result needs to be used again.

Ideally I would like to have the following which just sets the a, b and c as values but allows me to have subsequent values depending on preceeding ones, so exacly what With will not let me do.

f[x_] := specialScope[
{a = g1[x, 37],
b = g2[a, Some -> Stuff],
c = g3[c, "foo"]},
h[c, "bar"]];

My current solution is to use Module, but I think that it creates more overhead than needed as the variables do not need to be mutable within the scope.

f[x_] := Module[
{a, b, c},
a = g1[x, 37];
b = g2[a, Some -> Stuff];
c = g3[c, "foo"];
h[c, "bar"]];

Is there something like that? I believe that Haskell's where supports this use case.

• Take a look at this question and answers therein: mathematica.stackexchange.com/questions/10432/… - I frequently use the LetL function by @LeonidShifrin from there. Other alternatives are the GeneralUtilities`Where function and the undocumented form of With (see answer by @MichaelE2 there) Aug 14 '19 at 8:58

An alternative approach. You could rewrite your function

f[x_] := h[g3[g2[g1[x, 37], Some -> Stuff], "foo"], "bar"];

as

f[x_] := h["bar"][g3["foo"][g2[Some -> Stuff][g1[x]]]]

or more legibly

F = h["bar"]@*g3["foo"]@*g2[Some -> Stuff]@*g1

Another way of doing this would be using Fold:

Fold[First[#2][#1, Last[#2]] &, x, {{g1, 37}, {g2, Some -> Stuff}, {g3, "foo"}, {h, "bar"}}]

So the function and the other arguments are paired visually…

Or one can create an operator form using Composition:

(h[#,"bar"]&)@*(g3[#,"foo"]&)@*(g2[#,Some->Stuff]&)@*(g1[#,37]&)