674 points by panic 2880 days ago | 354 comments on HN
| Neutral Editorial · v3.7· 2026-02-28 14:12:29
Summary Off-Domain Technical Content Neutral
This URL contains a detailed technical history essay about programming language assignment operators, with zero substantive connection to human rights. The content traces the evolution from ALGOL's ':=' notation to C's '=' operator. All HRCB section scores are ND (No Data) as the material is purely about computer science history and notation conventions.
The '=' sign is used because after that statement the variable will indeed have equality with the r-value.
Coincidentally in async languages like Verilog there is a another assignment operator '<=' which means that the variable won't take the new value until the next clock cycle (or more explicitly the next evaluation of the process block). '=' exists also and has the same meaning as with traditional languages.
Another interesting way to think about it is as "match". That is try to match the stuff on the right with the stuff on the left.
Take Erlang for example:
1> X = 1.
1
2> X = 2.
** exception error: no match of right hand side value 2
Notice variables are immutable (not just values themselves). Once X becomes 1, it can only match with 1 after that. You might think this is silly or annoying, why not just allow reassignment and instead have to sprinkle X1, X2 everywhere. But it turns out is can be nice because it makes state updates very explicit. In complicated applications that helps understand what is happening. And it behaves like you'd expect in math, in the sense that X = X + 1 doesn't make sense here either:
1> X = 1.
1
2> X = X + 1.
** exception error: no match of right hand side value 2
3>
It does pattern matching very well too, that is, it matches based on the shape of data:
1> {X,Y} = {1,2}.
{1,2}
2> X.
1
3> Y.
2
4>
In other languages we might say we have assignment and destructuring but here it is rather simple it's just pattern matching.
I don't understand the LISP line at all. I would have said "LET", "SET", and "EQUAL" (since it's talking about numbers). Is there something I'm missing?
EDIT: It's since been changed to "let", "set", "equal" (but still lower-case).
I actually rationalized the '=' symbol in assignment into the following statement, "Let 'left hand side' be equal to 'right hand side'". Using this wording resolves some dissonance around its overloaded usage.
You'll have to pry my BCPL heffalumps out of my cold, dead hands.
The operator " = >" (pronounced "heffalump") is
convenient for referencing structures that are
accessed indirectly. The expression
a=>s.x is equivalent to the expression (@a)»s.x.
Here's what Niklaus Wirth (Pascal, Modula-2, Oberon) said about using the equal sign for assignment:
> A notorious example for a bad idea was the choice of the equal sign to denote assignment. It goes back to Fortran in 1957 and has blindly been copied by armies of language designers. Why is it a bad idea? Because it overthrows a century old tradition to let “=” denote a comparison for equality, a predicate which is either true or false. But Fortran made it to mean assignment, the enforcing of equality. In this case, the operands are on unequal footing: The left operand (a variable) is to be made equal to the right operand (an expression). x = y does not mean the same thing as y = x. Algol corrected this mistake by the simple solution: Let assignment be denoted by “:=”.
> Perhaps this may appear as nitpicking to programmers who got used to the equal sign meaning assignment. But mixing up assignment and comparison is a truly bad idea, because it requires that another symbol be used for what traditionally was expressed by the equal sign. Comparison for equality became denoted by the two characters “==” (first in C). This is a consequence of the ugly kind, and it gave rise to similar bad ideas using “++”, “--“, “&&” etc.
From Good ideas, through the Looking Glass by N. Wirth:
I've been programming professionally for 25 years using mostly C inspired languages, and I will still regularly write "if(foo = bar)" on a daily basis and not notice until there's an error. It's easily the most common syntax error I write, followed closely by using commas in my for-loop as apparently it looks like a function to my fingers: "for(x=0, x<10, x++)"
During the public comment period for the original ANSI C standard, we had at least one request to add ":=" as an alternate assignment operator. We declined, but I personally would have supported that. The use of = and == is one of my least favorite bits of C syntax.
I always liked DHH's take on these sorts of arguments (paraphrasing): who the hell cares? Once you know the purpose of the '=' how often do you make mistakes reading or writing code?
Whereas Java is all about protecting developers from themselves, Ruby (for example) let's you get away without variable type declaration because at the end of the day, how often do you not know whether a particular variable is a string or an integer? Enough to justify enforcing declaring everything at the outset or receiving errors throughout your code?
That's not to say that those enforcements never make sense. I think that structured languages like Java are better for larger teams where enforcing standards is more important than a smaller team. But there are other ways to do that and a lot of time the rules make development a horrible experience.
My first programming teacher insisted we call the single = sign the 'gets' operator. So
int x = 10
int y = x
would read in English as x gets 10, y gets x.
Shortly after that class I took a break from any coding. When I went to another school I saw Softmore and Junior level programmers still struggling with this. I retained the habit of using 'gets'and never had a problem with this.
>A common FP critique of imperative programming goes like this: “How can a = a + 1? That’s like saying 1 = 2. Mutable assignment makes no sense.” This is a notation mismatch: “equals” should mean “equality”, when it really means “assign”.
I sort of disagree with this. Many functional languages pull heavily from lambda calculus and other forms of mathematics. In math, "a = a + 1" isn't the same as "1 = 2". The issue isn't equality, it's that you're trying to rebind a bound variable, which isn't possible.
In other words, rebinding a bound variable is not the same as "1 = 2".
I think they are forgetting that a lot of languages take que's from old math textbooks where you would see function definitions written as "f(x) = nx + b" or "y = nx + b" and constant assignment with a "c = <number>" notation.
if you want to calculate the output of a function into a data table(which is what early computers were often doing) iterating a variable x over a f(x) = xn + b(with n and b being fixed constants) is exactly how you would do it on paper so it's probably a case of computers emulating applied math rather then simply being pure theory machines.
Simple, programming is like using Old Speech, the language of Dragons. We cannot lie in that language and therefore when we make a statement it becomes true. (In case I'm being too obtuse.. Earthsea)
But really, this is such a pedantic discussion, trying really hard to not get sucked in.
Another question is why the assignment is from right to left when the natural direction would rather be left to right, like 2+2 => x to store the value 4 in x.
I was told once by a math professor that it is a habit inherited because we use Arabic numbers/maths which were really meant to be read from right to left. Don’t know if the theory has any merit.
As the article points out, the big first programming languages, FORTRAN, COBOL, LISP, and Algol each had their own way of doing variable assignment. FORTRAN used "=" and Algol used ":=" the other two languages used commands or functions to set or bind the values of variables.
In the mid 60's, when I started programming, most programs had to be keypunched. (There was paper tape entry and Dartmouth's new timesharing system using BASIC, but these weren't in very widespread use.) Until 1964, the keypunch machine in use (IBM 026) had a very limited character set. This is the reason that FORTRAN, COLBOL, and LISP programs were written in upper case. Even the fastest "super" computer of the time, the CDC 6600, was limited to 6-bit characters and didn't have lower case letters.
Naturally, symbols like "←" or "⇐" weren't available, but even the characters ":" and "<" were not on the 026 keypunch keyboard and so ":=" or "<-" were much harder to punch on a card.
These early hardware limitations influenced the design of the early languages, and the early programmers all became accustomed to using "=" (or rarely ":=" or SET) as assignment even though "⇐" might have been more logical. The designers of subsequent programming languages were themselves programmers of earlier languages so most simply continued the tradition.
I'm sure I've mentioned it here before, but in my favorite Erlang talk I design the language with the equal sign as my core construct.
Since the equal sign is an assertion of truth, and since assertions cause a crash on failure, you can pretty much derive the rest of the language/BEAM characteristics from that.
Yeah I was pretty surprised when I found out that assignment and equality can be totally syntactically separate and nonambiguous with miminal language-design effort. Just get rid of the silly idea of allowing expressions as statements.
Although even then it's nice to use different symbols because they are different meanings. I don't like it when a word has different meanings depending on context.
also, there's no hand balance on either of the composite symbols. := is my right pinky, <- is different fingers, but bottom and top row. neither exactly flows from the fingertips.
And if the goal of mathematicians and programmers were the same, this might be a compelling argument. In no sense do I see my programs as giant algorithms, they're machines, and as such require different syntax to construct.
> But mixing up assignment and comparison is a truly bad idea
Then := is also a bad decision. You should use the fully historically supported form of 'let <variable> = <value>'. Both := and == suffer from the fact that if you omit a single character, you change assignment into comparison or vice versa.
It strikes me as a nitpicky argument for it's own sake.
I don't understand it either. I suspect the author does not know Lisp, and badly misunderstood an explanation from someone who does.
I think that Lisp doesn't even fit well into that table, since putting LET in the first column would implicitly bring in declaration as well, much of the time SET in the second column would be misleading since mutation is so often done implicitly by a looping construct like DO, DOLIST, DOTIMES or whatever (no SET in sight), and there are all sorts of equality tests one could use.
Edit: ok, I see the author has both updated it, and is specifically talking about Lisp 1.5 which does not have the looping constructs. On the other hand, it at least has a bunch of alternatives to SET that probably should be listed as well, like RPLACA, RPLACD, ATTRIB, and so on. I think that even in Lisp 1.5 variables could be declared in different ways to using LET (like rebinding a variable passed in to a function, so the assignment was in invocation).
There is a Ruby idiom[0] that I find quite useful (esp. combined with a linter such as Rubocop), leveraging optional parens as a marker of intentional assignment:
# bad
if v = array.grep(/foo/)
do_something(v)
# some code
end
# good
if (v = array.grep(/foo/))
do_something(v)
# some code
end
1> {X, X} = {1, 1}.
{1, 1}.
2> {Y, Y} = {1, 2}.
** exception error: no match of right hand side value {1,2}
If you're wondering what the use of that is, here's an Erlang "drop all occurrences of an element X from a list" function. (Prerequisite knowledge for this: Erlang functions have several clause-heads; which one is executed on each call depends on which one is able to successfully bind to—i.e. = with—the arguments.)
In other words—first, set up an accumulator. Then, go through the list, and if X can bind in both positions, skip the element; otherwise (i.e. if Y != X) then shift Y into the accumulator. At the end, reverse the accumulator (because you were shifting.)
My issue with the larger teams vs. smaller teams argument is that the price of success is finding yourself with the wrong tools because successful projects usually grow large teams. The counterargument is that the price of choosing the right tools for a large team may be failure when those tools don't let you execute quickly enough. I don't really buy this trade-off. Maybe it is true for the specific language of Java for a small team (I'm not so sure it is) or Ruby for a large team (I have lived this and found it to be anecdotally true, so I do buy this one), but I don't believe you have to make this choice; I believe there can be languages that are productive for small teams while avoiding foot-guns for large teams.
At one point in my career, I was afflicted with a Wirth-designed language. His opinion on what constitutes good language design does not carry much weight with me. In particular, I can tell you that the extra typing of Pascal over C really does matter over a couple of years. And that := for assignment is a major pain when your left pinky finger is out of action for weeks, and you still need to hit shift for every assignment.
But then we get to this line:
> Because it overthrows a century old tradition to let “=” denote a comparison for equality
In what sense is "=" for comparison a century old? Hasn't it been used for "things that are equal" for multiple centuries? If so, and if that's distinct from "for comparison", then isn't for comparison also a new, non-standard use?
Does anyone understand what he's on about in this quote?
> In math, "a = a + 1" isn't the same as "1 = 2". The issue isn't equality, it's that you're trying to rebind a bound variable, which isn't possible.
"=" means equality in math; a = a + 1 is the same as 1 = 2 because if you subtract a from both sides and add 1 to both sides you get 1 = 2.
Lambda calculus has the concept of binding variables, but it doesn't use "=" for that, it uses application of lambda forms. It's the same idea that's applied in some variants of Lisp, where LET is a macro such that (let ((x 1) (y 2)) ...) expands to ((lambda (x y) ...) 1 2).
The way it plays out is that rebinding is perfectly fine, because it's not really any different from binding in the first place. The same way that
This works for initialization, but not for reassignment. Most notably, it is absurd for statements like x = x + 1. Meanwhile, such self-referential updates are very common in imperative programming, so anyone designing the language would come across this.
> how often do you not know whether a particular variable is a string or an integer?
In someone else's code? 100% of the time. I was once a diehard Ruby believer, and I still use it for small programs. But having had the singular displeasure of working on a huge Ruby codebase (pre-JVM Twitter), I have learned, in the hardest possible way, that untyped languages are absolute nightmares when more than one person is involved.
Editorial Channel
What the content says
ND
PreamblePreamble
Content is technical history of programming language assignment operators. Not substantively related to UDHR preamble or human dignity.
ND
Article 1Freedom, Equality, Brotherhood
Content does not address human equality, dignity, or rights of all humans.
ND
Article 2Non-Discrimination
Content is about programming language history, not non-discrimination or distinction of any kind related to human rights.
ND
Article 3Life, Liberty, Security
Content does not address right to life, liberty, or security of person.
ND
Article 4No Slavery
Content is not related to slavery or servitude.
ND
Article 5No Torture
Content does not address torture or cruel/inhuman/degrading treatment.
ND
Article 6Legal Personhood
Content is not related to right to legal recognition or personhood.
ND
Article 7Equality Before Law
Content does not address equal protection before law.
ND
Article 8Right to Remedy
Content does not address effective remedy for rights violations.
ND
Article 9No Arbitrary Detention
Content is not about arbitrary arrest or detention.
ND
Article 10Fair Hearing
Content is not related to fair trial or due process.
ND
Article 11Presumption of Innocence
Content is not about criminal liability or retroactive laws.
ND
Article 12Privacy
Content does not address privacy, family, correspondence, or reputation.
ND
Article 13Freedom of Movement
Content is not about freedom of movement or residence.
ND
Article 14Asylum
Content is not about asylum or refuge.
ND
Article 15Nationality
Content is not about nationality or citizenship.
ND
Article 16Marriage & Family
Content is not about marriage or family rights.
ND
Article 17Property
Content is not about property rights.
ND
Article 18Freedom of Thought
Content is not about freedom of thought, conscience, or religion.
ND
Article 19Freedom of Expression
While the content is expressed on a blog (platform for expression), the subject matter is about programming language history, not about freedom of expression or information rights.
ND
Article 20Assembly & Association
Content is not about freedom of assembly or association.
ND
Article 21Political Participation
Content is not about political participation or democracy.
ND
Article 22Social Security
Content is not about social security or welfare rights.
ND
Article 23Work & Equal Pay
Content is not about labor rights, employment, or working conditions.
ND
Article 24Rest & Leisure
Content is not about rest, leisure, or working hour limitations.
ND
Article 25Standard of Living
Content is not about food, clothing, housing, or health.
ND
Article 26Education
While the content is educational, it is technical education in programming language history, not addressed to universal education rights or access.
ND
Article 27Cultural Participation
Content is about programming language design, not about cultural participation or scientific benefits.
ND
Article 28Social & International Order
Content is not about international order or human rights framework.
ND
Article 29Duties to Community
Content is not about duties to community or limitations on rights.
ND
Article 30No Destruction of Rights
Content is not about limitation or destruction of rights.
Structural Channel
What the site does
ND
PreamblePreamble
No structural signals observable regarding UDHR preamble principles.
ND
Article 1Freedom, Equality, Brotherhood
No structural signals observable regarding equality or human rights.
ND
Article 2Non-Discrimination
No structural signals observable regarding discrimination or human rights protections.
ND
Article 3Life, Liberty, Security
No structural signals observable regarding life or security rights.
ND
Article 4No Slavery
No structural signals observable regarding slavery or servitude.
ND
Article 5No Torture
No structural signals observable regarding torture or inhumane treatment.
ND
Article 6Legal Personhood
No structural signals observable regarding legal personhood.
ND
Article 7Equality Before Law
No structural signals observable regarding legal equality.
ND
Article 8Right to Remedy
No structural signals observable regarding legal remedies.
ND
Article 9No Arbitrary Detention
No structural signals observable regarding arrest or detention.
ND
Article 10Fair Hearing
No structural signals observable regarding fair trial.
ND
Article 11Presumption of Innocence
No structural signals observable regarding criminal justice.
ND
Article 12Privacy
No structural signals observable regarding privacy.
ND
Article 13Freedom of Movement
No structural signals observable regarding movement rights.
ND
Article 14Asylum
No structural signals observable regarding asylum rights.
ND
Article 15Nationality
No structural signals observable regarding nationality rights.
ND
Article 16Marriage & Family
No structural signals observable regarding family rights.
ND
Article 17Property
No structural signals observable regarding property.
ND
Article 18Freedom of Thought
No structural signals observable regarding freedom of conscience.
ND
Article 19Freedom of Expression
No structural signals regarding freedom of expression; content is published openly.
ND
Article 20Assembly & Association
No structural signals observable regarding assembly rights.
ND
Article 21Political Participation
No structural signals observable regarding political participation.
ND
Article 22Social Security
No structural signals observable regarding social rights.
ND
Article 23Work & Equal Pay
No structural signals observable regarding labor rights.
ND
Article 24Rest & Leisure
No structural signals observable regarding rest or leisure.
ND
Article 25Standard of Living
No structural signals observable regarding welfare.
ND
Article 26Education
Content is published freely, but no signals regarding education access or rights.
ND
Article 27Cultural Participation
No structural signals observable regarding cultural or scientific rights.
ND
Article 28Social & International Order
No structural signals observable regarding international order.
ND
Article 29Duties to Community
No structural signals observable regarding community duties.
ND
Article 30No Destruction of Rights
No structural signals observable regarding rights protections.
Supplementary Signals
How this content communicates, beyond directional lean. Learn more
build aba2bc8+myve · deployed 2026-02-28 16:36 UTC · evaluated 2026-02-28 16:29:11 UTC
Support HN HRCB
Each evaluation uses real API credits. HN HRCB runs on donations — no ads, no paywalls.
If you find it useful, please consider helping keep it running.