The "=" in the declaration of Haskell functions isn't considered an assignment, but a binding of a name to a definition. Usually when you have assignments, you are permitting a name to be rebound to a different value than it had originally. In Haskell you cannot rebind a name. Essentially "=" in Haskell is more like the operator's use in defining mathematical functions.
Due to how scoping and how do-comprehensions actually work, you can do some things that look like assignment, but are really creating a new binding in a nested scope. But if you try to bind a name twice in the same scope, the program fails to compile.
I intuitively knew this was the case, but wanted to do some research before posting an answer. The distinction between naming and assignment can be a little subtle. And you beat me to the punch.
So you'd say something like 'main is the function that evaluates the expression...' Traditional assignment would be something more like 'the value stored in the container main now becomes the expression...'
What definition does "assignment" have other than "a binding of a name to a definition"?
Keep in mind that there are at least three popular definitions of the = operator:
1. In C or C++, = handles assignment and mutation (no re-assignment):
int x = 1; /* assignment */
x = 2; /* mutation */
2. In Python, = handles assignment and re-assignment (no mutation):
x = [1] # assignment
x = [2] # re-assignment
3. In Haskell, = handles assignment (no re-assignment or mutation)
let x = 1 in
Just because Haskell's = operator does not behave like C's = operator is no reason to claim Haskell does not support assignment. In fact, that is the only thing Haskell's = operator supports.
This is now a semantic discussion - which is not necessarily a bad thing, we should just know that up front.
To me, the statement "programming without assignment" implies programming without making multiple assignments to the same name - mutation or re-assignment. I don't know if the programming language community has agreed-upon semantics for the word "assignment," but to me, it implies re-assignment and mutation. What you call "assignment" I think of as binding - that is, the lvalue gets bound to the rvalue.
The technical terms are 'variable assignment' and 'single assignment'. 'Assigment' is generally considered to mean the former unless it's qualified.
From a pedantic perspective, you can't really disagree with the statement 'haskell supports single assignment.' But equally as pedantic, most actual text reads 'FP doesn't support variable assignment', including the original article above.
I don't have a copy of the Ravi Sethi book to see what the exact text of his exact statement was.
To me, the argument to convince oneself that Haskell's = "assigmnent" is not the same beast as C's and Python's is the following: the binding it defines is also valid in code before it.
In C or Python, this would print 0 or undef or some random contents. In Haskell, it prints 42. The "assignment" reaches back to instructions before itself. Which ought to hint it's not really an assignment in the (my) usual sense of the word.
I think the other replies have answered your question well enough, but your definitions for = are a litle off. The cases 1 and 2 are really the same thing. In Python the names are references to objects. Assignment in Python is like using pointers in C. You are doing mutation, what you mutate is the value of a pointer (and probably some other oddsand ends for garbage collection).
I was going to say the same thing - that cases 1 and 2 are really the same thing - when I realized that's not quite accurate. In his terms, mutation is a special case of re-assignment. That is, re-assignment allows re-binding a name to an entirely different value which may have a different type. Mutation is then re-assignment where the types happen to be the same.
Due to how scoping and how do-comprehensions actually work, you can do some things that look like assignment, but are really creating a new binding in a nested scope. But if you try to bind a name twice in the same scope, the program fails to compile.