I'd not heard of this before, but apparently some people don't like Erlang's "single assignment" semantics. What this means is a "variable" actually isn't. A variable becomes "bound" to a value via pattern matching. Once bound to a value, that same variable in that same lexical scope cannot be re-bound to some other value. Within that scope that variable must subsequently match successfully against the value it currently has.
What appears to be an "assignment" in Erlang is really just the simplest kind of pattern match. Below X10
is matched against foo(X)
and since X10
is not yet bound, it becomes bound to the value of the function.
f(X) ->
X10 = foo(X),
X15 = fab(X10),
X20 = bar(X15),
baz(X20).
Some people are concerned this could lead to uncomfortably renumbering variables, say, when the function fab
should be added in the function above.
I dunno. This seems contrived, or at least rare, or maybe machine-generated. If you just have to "re-number" a few variables, no big deal. If you have to renumber too many variables then you'd be better served by decomposing an overly long function that also suffers from inexpressive variable names.
The author of the post is apparently writing a ruby-ish language for the Erlang runtime. More power to you.
But there is a lot to be said for a simple, mostly functional language that wants you to keep functions short. When I am writing in an imperative, even object-oriented, language, I want my procedures to be short. And I want one variable to have one value. The code is more "tractable" when short, and variables don't vary.
Then the name becomes essentially a mnemonic for the value.
The overwhelming majority of programmers have only used languages with multiple assignment.The overwhelming majority of programmers over my 25+ years of programming experience write procedures that are much too long, even when they are otherwise well-written. That's the first bad habit people seem to adopt, or never learned to avoid. I've worked with long, run-on procedures in every language I've encountered.
Lisp? Yes. Smalltalk? Yes. Pascal? Yes. Even these languages with their gifts for brevity cannot overpower the usual programmer's inaesthetics.
Understanding what many, small procedures do is always better than understanding what one or a few long, run-on procedures do. The payoff is there because you learn what you can put aside, and what you should pay attention to. On the other hand, long, run-on, convoluted noise is always with you. You never can put any of it aside - you have to revisit, remember, and ultimately revolt and/or refactor.
In an imperative language a variable should take multiple values only as part of an iterative statement. In the statement "for i = ...
" the variable i
should only be updated by this statement. Or in "while someCondition ...
" the variable someCondition
should be assigned an initial value before the while
and it should be updated immediately before the while
re-evaluates its decision to iterate again.
Write short functions. Use the shortest name that yet conveys the meaning or purpose of its value. Assign them once even if your language is imperative. Generally this will keep your stress level low, as well as the levels of those who pick up your code later.
7 comments:
Yes, that's all. Write your code well and than Erlang is good enough for you. I like Erlang because it is pushing your code to better quality.
Suffixing variable names with numbers is a terrible practice in itself.
Maybe the problem is that (Ruby, Java) programmers don't know how to rewrite an iterative algorithm that uses mutable variables as a tail-recursive algorithm with accumulator parameters.
Steve Vinoski addressed this problem in a previous post:
http://steve.vinoski.net/blog/2008/03/10/damien-katz-criticizes-erlang/
I use reassignment a lot when decomposing awkwardly long expressions into multiple expressions, or performing conversions. A common example would be:
i = int(i)
I can understand why that seems a little wrong, but I also don't really want to create a distinct notion of the-number-before-being-converted-to-an-int and the-actual-number. There aren't two usefully different things there. Another example might be converting an empty string to a null object (or vice versa). That I spend some time at the beginning of functions working out little details of the inputs, error checking, etc, doesn't make me feel like the function is any logically longer or needs to be decomposed.
What this means is a "variable" actually isn't."
We programmers have confused the issue. The term "variable" doesn't mean the value contained may change, but rather that the value it will contain may be different. In Joe Armstrong's book he briefly mentions how in mathematics a variable always has the same value; Erlang is the same way.
As a total Erlang noob I'm not able to speak about the relative merits of single- versus multiple-assignment, but in reading through tutorials and books it occurs to me that single-assignment is a feature, with which you can create control structures and other interesting behavior (why else is the otherwise-ubiquitous "if" statement so trivialized in Erlang?)
Actually,
1. To create control structures you just need closures;
2. "if" is much less used because of pattern matching.
Both are very nice features which are completely orthogonal to single-assignment (less so for pattern-matching).
One clear benefit I forgot to mention was that with this form of variable renaming is that you clearly “see” the progression of data and who may have modified it. It is also very clear which version of the data you are using. A typical use is when passing state through a function:
foo([A|As], State0) ->
{X,State1} = bar(A, State0),
{Y,State2} = baz(A, State1),
{Rs,State3} = foo(As, State2),
{[{X,Y}|Rs],State3}.
Wrote an extended comment around this on http://tonyarcieri.org/articles/2008/07/26/the-single-assignment-cargo-cult.
Post a Comment