Why modules with no variables?

41

14

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.

Reb.Cabin

Posted 2012-07-12T11:12:56.007

Reputation: 8 501

This might be a place to start reading about scoping constructs: http://mathematica.stackexchange.com/questions/559/what-are-the-use-cases-for-different-scoping-constructs

– image_doctor – 2012-07-12T11:17:03.813

1I 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 – Rojo – 2012-07-12T11:18:05.107

5While 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. – Oleksandr R. – 2012-07-12T11:28:50.777

3As a practitioner of terse coding a hate this format and do my best to stamp it out whenever I see it. – Mr.Wizard – 2012-07-12T21:15:09.073

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. – Reb.Cabin – 2016-04-13T19:52:42.080

Answers

21

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 CompundExpression) 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.

Leonid Shifrin

Posted 2012-07-12T11:12:56.007

Reputation: 108 027

2...which also doesn't avoid the use of CompoundExpression – Rojo – 2012-07-12T11:23:23.980

@Rojo Sure it doesn't – Leonid Shifrin – 2012-07-12T11:23:53.940

4I 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. – Reb.Cabin – 2012-07-12T11:36:32.730

1@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 – acl – 2012-07-12T11:49:16.320

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 – Rojo – 2012-07-12T21:37:04.840

Leonid, I don't understand your update. Specifically, is Module simply an arbitrary "label" for side-effect-containing code, or is there some deeper reason "Module really signals ... a piece of code where side effects are present or expected." ? Since Module is commonly used in side-effect-free code wouldn't another "label" be better? – Mr.Wizard – 2012-08-24T07:50:37.510

@Mr.Wizard Yes, Module is such a label, at least to me. And the reason is that Module with variables for sure is used for side effects in the majority of cases (except when Module- generated variables are used as tags of some kind). Using Module with a side-effect-free code does not seem to make much sense to me, and I never do it. – Leonid Shifrin – 2012-08-24T08:14:30.320

I wonder if we are operating with different definitions of code side-effects. Could you clarify? – Mr.Wizard – 2012-08-24T08:21:40.487

@Mr.Wizard Assignements to variables, printing, anything that changes the state of the world. In any case, I think the main logic in the cited text is very straightforward. Wrapping a single side-effect-free statement in Module (as in the question) does not make much sense to me. – Leonid Shifrin – 2012-08-24T08:56:51.237

By that definition I'm quite surprised and a bit confused that you say: "Using Module with a side-effect-free code does not seem to make much sense to me, and I never do it." Chat seems to be down, but maybe later we can talk about it. – Mr.Wizard – 2012-08-24T09:55:41.763

@Mr.Wizard Sure, let's talk later. – Leonid Shifrin – 2012-08-24T10:14:10.780

@Leonid Did we ever talk about this? I'd like to talk now, if you've got time. – Mr.Wizard – 2013-07-17T08:57:53.740

@Mr.Wizard Sorry, right now is bad. Could you please ping me another time (e.g. in a few hours from now)? – Leonid Shifrin – 2013-07-17T09:05:05.760

26

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.

image_doctor

Posted 2012-07-12T11:12:56.007

Reputation: 9 964

2I would guess the no-var overheads are from setting up the "nursery," if you will, for the bindings that never show up. – Reb.Cabin – 2012-07-12T12:49:51.237

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 – Rojo – 2012-07-12T21:29:00.090

Weirdly enough, in my recent test Identity[sth] seems slower than Block[{}, sth], and With haha – Rojo – 2012-07-12T21:31:18.870

@Rojo Your wish ... – image_doctor – 2012-07-13T07:27:30.997

1I 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. – Reb.Cabin – 2012-07-16T01:36:07.650

@Rojo Also f6[x_]:={x^2;x^3;}, and the f7overlySuperfluous:=DynamicModule[{},x^2;x^3;]. – István Zachar – 2013-04-03T20:30:39.447

@IstvánZachar caaaareful, those last 2 return something different, they don't just group :P – Rojo – 2013-04-03T20:59:16.963

@Rojo: Sure, but if it comes to grouping the sky is the limit (for highest possible AbsoluteTiming) :) – István Zachar – 2013-04-03T21:20:57.480

10

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...

Albert Retey

Posted 2012-07-12T11:12:56.007

Reputation: 22 455

+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. – Leonid Shifrin – 2012-09-04T09:24:29.017

@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? – Albert Retey – 2012-09-06T12:54:30.220

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. – Leonid Shifrin – 2012-09-06T13:26:11.247