42
$\begingroup$

I was reading some code, in particular, recipe 4.13 on unification pattern-matching in Sal Mangano's Mathematica Cookbook, and there were many instances of Modules with no variables in them, such as

Lookup[x_] := Module[{}, x /. $bindings]

i didn't understand the point. Why not just write

Lookup[x_] := x /. $bindings

? Sal uses such modules frequently, so I'm supposing there is some deep reason I'm missing, hence the question here.

$\endgroup$
5
  • $\begingroup$ This might be a place to start reading about scoping constructs: mathematica.stackexchange.com/questions/559/… $\endgroup$ Jul 12, 2012 at 11:17
  • 1
    $\begingroup$ I don't think there's a big point, but I'm commenting instead of answering bacause I'm not sure. If there's no particular need for ultra-high efficiency it makes no difference, and it may be his coding style, to start off writing locals, or to be prepared in case he later wants to add locals easily. Also some times when I have to parenthesise a definition a:=(b;c) I wish I had a module there to make it neater. Let's wait to see if there's a reason we're missing $\endgroup$
    – Rojo
    Jul 12, 2012 at 11:18
  • 6
    $\begingroup$ While a valid stylistic choice, this construct seems to have disproportionately more currency among novice users. Sometimes I worry that it's an example of cargo-cult programming, perhaps a result of the fact that the nuances of Mathematica's scoping constructs are not so easy to understand. $\endgroup$ Jul 12, 2012 at 11:28
  • 3
    $\begingroup$ As a practitioner of terse coding a hate this format and do my best to stamp it out whenever I see it. $\endgroup$
    – Mr.Wizard
    Jul 12, 2012 at 21:15
  • $\begingroup$ I was amazed to see the following variation on the theme: Block[{},someExpression]/;True in question 25522. That's a new level of no-op-ed-ness, the /;True at the end. It's by the respected @Rojolalalalalalalalalalalalala, so I'm inclined to believe there is something more going on that I don't understand. $\endgroup$
    – Reb.Cabin
    Apr 13, 2016 at 19:52

4 Answers 4

22
$\begingroup$

For a single code statement, this is probably an overkill. If you have two or more of them, you have to group them in any case. CompoundExpression is one obvious choice, such as

f[x_]:=
  (
    Print[x];
    x^2
  )

Instead, you could also do

f[x_]:=
  Module[{},
    Print[x];
    x^2
  ]

which is what I personally often prefer. Apart from some stylistic preferences, this may make sense if you anticipate that your function will grow in the future, and some local variables will be introduced.

EDIT

There is a more important point, which was escaping me for a while but which I had on the back of my mind. I will quote Paul Graham here (ANSI Common Lisp, 1996, p.19):

When we are writing code without side effects, there is no point in defining functions with bodies of more than one expression. The value of the last expression is returned as the value of the function, but the values of any preceding expressions are thrown away.

So, what Module really signals (since it is better recognizable than CompoundExpression) is a piece of code where side effects are present or expected. And, at least for me, having an easy way to locate such parts in code when I look at it is important, since I try to minimize their presence and, generally, they tend to produce more bugs and problems than side-effects-free code. But, even when they are really necessary, it is good to isolate and clearly mark them, so that you can see which parts of your code are and are not side-effects free.

$\endgroup$
13
  • 5
    $\begingroup$ ...which also doesn't avoid the use of CompoundExpression $\endgroup$
    – Rojo
    Jul 12, 2012 at 11:23
  • 2
    $\begingroup$ @Rojo Sure it doesn't $\endgroup$ Jul 12, 2012 at 11:23
  • 4
    $\begingroup$ I see; it's just pro-active programming: getting ready to make changes you're going to make later even though you don't know now what those changes will be. It's in lieu of parentheses in your example. It's the kind of thing I tend to put in while developing , but take out of finished code. That partially explains my surprise at seeing it in the book -- it's not the kind of thing I would put in published code unless I were trying to make exactly the point about proactive coding. $\endgroup$
    – Reb.Cabin
    Jul 12, 2012 at 11:36
  • 1
    $\begingroup$ @Reb.Cabin but to get rid of it in finished code, you'd need to replace Module[{},] constructs by () constructs. It's no neater, and does not express the intend more clearly. I don't see any advantage to replacing Module with parentheses, apart from a possible small performance difference. In any case this is a stylistic choice $\endgroup$
    – acl
    Jul 12, 2012 at 11:49
  • $\begingroup$ I'd like to suggest somewhere that if you really want to avoid parentheses and get the square brackets ready for future change to Module and friends, you can always use Unevaluated for SetDelayed and Evaluate for Set $\endgroup$
    – Rojo
    Jul 12, 2012 at 21:37
29
$\begingroup$

Here is the almost obligatory timing response, it probably doesn't generalise very broadly but perhaps is indicative in some respects:

(* no variables *)
f1[x_] := (x^2; x^3;)
f2[x_] := Module[{}, x^2; x^3;]
f3[x_] := Block[{}, x^2; x^3;]
f4[x_] := With[{}, x^2; x^3;]

(* With variable definition *)

f2[x_] := Module[{y = 0}, x^2; x^3;]
f3[x_] := Block[{y = 0}, x^2; x^3;]
f4[x_] := With[{y = 0}, x^2; x^3;]

(* For the two cases the following statement was executed *)
AbsoluteTiming[Do[#[5];, {10000000}];] & /@ {f1, f2, f3, f4}

The timings, in seconds, were as follows:

Mathematica graphics

(...) is fastest, Module seems to carry some overhead when variable creation is called for when compared to other methods, otherwise there is little to choose between Module,Block or With.

I've extended the table a little and added entries for 2,3 and 4 variables. A late addition was for f5[x_]:=Identity[x^2; x^3;]:

Mathematica graphics

There seems to be a reasonable correlation between the number of variables and time taken. The immutable nature of With's "coniables" seems to carry less overhead.

$\endgroup$
8
  • 2
    $\begingroup$ I would guess the no-var overheads are from setting up the "nursery," if you will, for the bindings that never show up. $\endgroup$
    – Reb.Cabin
    Jul 12, 2012 at 12:49
  • $\begingroup$ You forgot to test f5[x_]:=Identity[x^2; x^3;]. If they are there to do nothing but pre-write square brackets and avoid parentheses, let them explicitly do nothing $\endgroup$
    – Rojo
    Jul 12, 2012 at 21:29
  • $\begingroup$ Weirdly enough, in my recent test Identity[sth] seems slower than Block[{}, sth], and With haha $\endgroup$
    – Rojo
    Jul 12, 2012 at 21:31
  • $\begingroup$ @Rojo Your wish ... $\endgroup$ Jul 13, 2012 at 7:27
  • 1
    $\begingroup$ I have some evidence that in With the variables are erased and their values are substituted in-line in the body. Try something like With[{x=42},x=37] and you should see a message about raw objects. Some more investigation could yield more evidence pro or con. $\endgroup$
    – Reb.Cabin
    Jul 16, 2012 at 1:36
11
$\begingroup$

I don't think that there is any deeper reason for using Module[{},body] instead of just (body). Technically you are only adding overhead, as small as it might be. From the stylistic point of view I think it just adds complexity, increases what has to be read and -- as your question clearly indicates -- raises questions and adds uncertainty. I don't see any reason to use it and find it somewhat unlucky to appear in a book about Mathematica programming, especially if there is no explanation why it is there (I don't know the book, maybe there is an explanation?).

As it is one of the rare cases where I disagree with Leonids answer some words about his answer: I don't think that the use of Module is an indication of whether side effects occur or not in general code. Of course you can, as Leonid does, make it a convention in your code to use a Module with no local variables to indicate side effects, but without declaring that somewhere, it doesn't mean anything to "foreign" readers of the code.

Your example from Sal Manganos books looks like he doesn't use that convention, as Lookup only has one expression and most probably is side effect free (of course depending on how $bindings actually looks).

I would even think that the most common use of Module with local variables is to combine a set of expressions as these:

f[x_]:=(y=x^2;Exp[y]+y)

which have an (observable) side effect in such a way that they are effectively "side effect free", (combined to one side-effect free expression), e.g.:

f[x_]:=Module[{y},y=x^2;Exp[y]+y]

Of course this will generate a local variable and set it to a value (which are side effects) but that are to some extent implementation details and are of temporary and local nature only: to be able to create local variables without observable side effects to the outside world is the reason for scoping constructs to exist in the first place. So you could just as well argue it would be an obvious convention to indicate that a function is (effectively) side effect free if it does use a Module wrapper...

If I would write code for others to read, I'd probably rather use a comment or usage message or naming convention to document whether a function has side effects or not. Other than that, I prefer to not read any boilerplate code that actually doesn't do anything -- I usually find it hard enough to understand the code that does do something...

$\endgroup$
3
  • $\begingroup$ +1, indeed I use this as a convention. But, I think that side effects are still side effects, even when they are made with local variables and not easily observable from the outside. To me, the main difference that side effects bring is that the pieces which form the body of Module are generally no longer meaningful in isolation, and this impairs testability. Of course, it may not matter much on a small scale of a single function, but the style of thinking is still different. I agree that side effects would affect code much more had the pass-by-reference been used more frequently. $\endgroup$ Sep 4, 2012 at 9:24
  • $\begingroup$ @Leonid: thanks for the comment, and the +1 of course. I think we don't disagree about the fact that the use of local variables in a Module has side effects, due to how Module is implemented. I just wanted to emphazise that these are often not observable and have a different "quality" than e.g. the obvious and intended side effects of functions like Print or CreateDocument. I tried to clarify that your "interpretation" of Module[{},...] is by convention and isn't something that has a deeper meaning that can be understood from how mathematica works, or would you disagree on this? $\endgroup$ Sep 6, 2012 at 12:54
  • $\begingroup$ No, of course I agree. Your comment actually made me realize that the problem of side effects is largely avoided in the large-scale mma programming simply because pass-by-reference semantics is not natural in mma, and requires Hold-attributes, so this is not how parameters are passed most of the time. So, the biggest problem of having functions dependent on the context on the large scale isn't actually there. And side effects on the scale of individual functions can only increase the complexity so much.I still prefer doing without them for better testability and composability though. $\endgroup$ Sep 6, 2012 at 13:26
2
$\begingroup$

CompoundExpression

If I need to group expressions I prefer the use of the FullForm of CompoundExpression, i.e. to use

CompoundExpression[
    x^2, 
    x^3
]

instead of

Module[
    {}, 
    x^2; 
    x^3;
]

Notice that

FullForm[Hold[Module[{}, x^2; x^3;]]]

enter image description here

With an unnecessary Null as 3rd argument.

Benchmark

There is a small but measurable performance cost for each added element in a an expression, and the simplest is the fastest.

f0[x_] := CompoundExpression[x^2, x^3]
f1[x_] := (x^2; x^3;)
f2[x_] := Module[{}, x^2; x^3;]
f3[x_] := Block[{}, x^2; x^3;]
f4[x_] := With[{}, x^2; x^3;]

TableForm[{#,First@RepeatedTiming[#[5], 90]} & /@ {f0, f1, f2, f3, f4}]

enter image description here

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.