2017-12-16

Variables

First, stop thinking about boxes.

Boxes are seductive, easy to think about, and horribly wrong. Every buffer overrun, and almost every other security hole in modern software, can be traced to a box that someone thought would be big enough---but wasn't.

In Python, you don't put things in boxes. Python represents your view on reality, and while you can virtually categorize things, it involves no physical moving (or, even worse, copying) of objects. Objects stay where they are; you just name them according to some scheme. They don't even know (nor care) whether they are named.

The beauty of Python is that the same philosophy works both up (namespaces, which map names to objects, are just ordinary objects, usually of type dict) and down (object's attributes are not contained inside it---they are just named from it, and they live separately).

So, names are just some objects (usually of type str) you associate with objects using some other objects (namespaces). Objects are obviously not created by names, they are created by evaluating literals. Every time [] is evaluated (possibly with something in between), a new list is made. So, evaluating [[0]*5]*7 makes two lists, an inner one and an outer one. Of course then outer[3] is the same list as outer[4]---what other list is there?

Of course, Python is allowed to cheat in the name of performance, as long as you don't notice the sleight of hand. Many objects constructed by literals are immutable, so Python doesn't have to construct a brand new number eight every time you write 8 in your code (and Python executes it)---it just gives you some number eight it has already constructed; you won't see any operational difference since there is no way you could change its value. (Of course, you can see a difference through is keyword, or id builtin, but that's just using the wrong tool---see Pass by what? for the crucial difference in philosophies in treating objects and values.)

Closely related to variables is the notion of changing things (after all, it's the etymology of the term "variable").

“change” has two meanings, which are different in Python. Let’s call them “replace” and “modify” (Python terminology is “rebind” and “mutate”, but here I think it can be profitable to depart from standard terminology.) Usually, when people are confused about variables, it's because they mixed up those two different meanings---using one instead of another, or thinking they are the same when they aren't.

As I said above, every rebinding is a mutation, but not of the same object. Rebinding of a name x is mutation of whatever namespace contains x. Also, up to some details at the bottom (you have to start somewhere) every mutation of an object is a rebinding of an object, but a different object. Mutation of x is a rebinding of some attribute (or component) of x.

In our new terminology, when you replace an object, you let it be, you just put something else in its place - or rather, use its name for something else. When you replace your car, your old car still continues to exist (unless it's garbage collected), but you just start calling some other car my_car.

a=1 means a is a name for an object 1. b=a means b is another name for the same object. b=3 means b is now no longer a name for that object, but for another object, 3. a is of course still the name for the same object - you haven’t changed a by replacing b.

On the other hand, when you modify an object, it is still the same object - you just changed (replaced, or possibly modified) some attribute or component of it. When you paint your car blue, it is still the same car, but my_car.color is now #00f instead of whatever it was before.

a=[1,2,3,4] means a is a name for an object, some concrete list that currently has four elements. b=a means b is a name for the same object. b[0]=7 replaces first component of b, modifying b, but it doesn't replace b: it’s still the same object. So a[0] is the first component of that same object, that is, 7.

2017-09-18

Pass by what?

"Passing objects by value" is a horrible oxymoron. As Stallman says for the term "intellectual property", it is a "seductive mirage". It tries to conflate things that were never meant to be conflated, and in fact cannot be conflated consistently.

It is a matter of a clash of two philosophies. In one of them, we handle values, which are unchangeable elements of abstract types viewed as mathematical sets, and functions acting on them are ordinary mathematical functions, producing new values without changing the old one in any way. f(x)=x^2+3x+5 surely doesn't change x. This naturally leads to a functional paradigm, where the very idea of "changing state" is either totally foreign, or at least requires explicit transfer of state (in this case, it is a namespace containing x) through the called function.

In another one, we handle objects, which are representations of real-world objects, elements of abstract classes as the blueprints of their specification, and as such absolutely changeable with the passing of time. Functions acting on them (methods) are not mathematical functions, but algorithms, in which the natural elementary step is changing the state of some object (or a construction of a new object---copying an object is a special case of this---but surely explicitly called, never implicit in something like a function call). This naturally leads to imperative paradigm, where the idea of "changing state" is fundamental and the state is always implicit, in the same way the real world is always implicit. When you ask your son to "go to the shop and buy 10 eggs", you don't pass him the Universe as a parameter (for goodness sake!), and you expect, as a matter of course, that after such an action is executed (if it doesn't raise a TheyHadNoMoreEggs exception), some external shop will have 10 eggs fewer. And you're perfectly fine with that. You must be, since it's how the real world works.

[Right abstraction level is crucial here. If a quantum physicist tells me that's not how the real world really works, I'll tell them they are mixing abstraction levels. Just as the canonical Python implementation is written in C, the canonical real world implementation is written in quantum mechanics. It can be written in different languages, though.]

Long time ago, people knew the difference very well. For instance, first versions of ALGOL had functions that created new values, and procedures which changed existing ones. Heritage of that thinking is still present in Pascal, which still syntactically separates functions and procedures, although without enforcing the rule that functions mustn't change values, nor the procedures must create new ones. Practicality made the conflation possible, even necessary, and a smartass making the BCPL saw they could eat their cake and have it too---for "values" in BCPL are just what fits in a processor word. (Arrays, and strings as a special case of them, cheat, in the sense of actually being a pointer.) A function receiving its arguments from the stack can equally well pick up a word representing the value, as a word representing its address. So, why not? If we add one-character tokens for converting value into address and back (# and !), they'll just drown in the syntactic noise of a new language, and we can use one concept to cover both philosophies. A path to hell, as they say, is paved with good intentions. :-]

C, like a "BCPL 3K", built on that. The characters are different (& and * instead of ! and #), and we finally have "true" arrays (including "true" string literals), and we got a few more characters to support them (", \, [, ],... necessitating the use of trighraphs since they weren't available in all the character sets), but the fundamental properties stayed the same. Now everyone understood that a "true" array should push its whole value onto the stack, but that seemed absolutely unacceptable in a performance-obsessed world---and besides, that way would make it impossible to call legacy BCPL code from C, which would kill C in the crib. So, as a compromise, and a token of backward compatibility with BCPL, arrays "convert into pointers at the slightest provocation", in the words of legendary Kernighan. Then, to actually pass values bigger than one processor word into functions, we got "struct"s, which could contain arrays, but they didn't decay into pointers since backward compatibility wasn't an issue, BCPL having no support for structs at all. So, structs are the first example of an "abstraction leak", when we finally saw that "transferring a value" into an "imperative function" will require non-trivial copying, but it was too late to go back to the old paradigm.

C++ just added a few more plates of good intentions onto the aforementioned path. As first "implementations" (in fact just a bunch of preprocessor macros) of C++ had "class" as simply an alias for "struct", it seemed obvious that objects, whatever they are, should copy themselves into functions---except, of course, in case that the functions are the methods on those same objects, that doesn't actually make any sense anymore. :-) So the silly "this"-rule was invented, saying that the object the method was called on wasn't copied, but other objects, as arguments of that methods, are. Now you have the situation that son.buy_eggs(10, shop) (after a successful execution) produces a world in which your son has 10 eggs, but the shop also has that same 10 eggs, since your son took them from the copy of the shop, that stopped existing as soon as the function stopped executing. You can only hope that he took them by value, not by reference, since otherwise you'd get a segmentation fault when you try to make an omelette. It's horrible, and at the same time a logical consequence of everything before it.

Of course, a total outrage when people realized what swindle they have been sold as "object programming" made Strostrup quickly patch C++ by introducing "references", so you could write son.buy_eggs(10, shop), and it would actually change the shop. That in turn produced a total consternation of C-experts, but they were quickly dismissed as old farts who can't keep pace with the modern programming philosophies.

Alas, backward compatibility is a bitch. That's why "this" is still a pointer and not a reference (I'm convinced, if Guido lent Bjarne his time machine, it would be the first embarrassment he'd go about fixing), and much more importantly, the declaration of set<Egg> buy_eggs(int n, Shop s) still means that the poor shop is copied at every call. That was masked by a single-character (to more easily drown it in the syntactic noise, of course) addition of "&" in front of "s" in the declaration (unfortunately, in the meantime ASCII won the encoding war and you couldn't just add new symbols at will, but hey, "&" can be reused for quasi-similar purpose), and an unwritten rule that passing by value is something yucky that you use only when you have to, and something you don't talk about in high-class OO social circles. :-P

The rest, as they say, is history. "const" grew besides "&" to pacify old farts at least a bit, then copy constructors were added to pacify everyone else, then smart pointers were added to pacify Herb Sutter and Dave Abrahams ("Want speed? Pass by value!", a legendary article that finally asked a long-awaited question of "Hey, are we sure we know what we're doing here?"), but that didn't really work because you can't pacify one Herb Sutter by trivial cosmetic surgeries, so C++11 (yes, in 2011:) finally makes a radical paradigm shift: copy constructors are yucky and inefficient, move constructors are the latest craze. And when you look at it closely, move constructors are a final admission that objects and values are fundamentally different kinds of animals, and it was wrong to ever try to put them in the same category.

What does the mainstream market offer today? Besides C++, which has jumped the shark so many times it's not funny anymore even to mock it? There's Java, that has totally accepted the inevitability of that conclusion, having a built-in support for BCPLian "primitive values" that fit into a "word" of its virtual machine, and a completely separate support for objects, passed by reference because that's the only sensible way to pass objects. We have Haskell, a functional language mocking the very idea of "changing objects", living in its own mathematical world of values where it's straightforward to write a least fixed point of search over uncountable spaces, but any I/O requires nontrivial category theory. And of course, we have Python: the only mainstream language that's completely on the other side. Every referrent is an object. Mathematical "values" (like the number five) are simulated by objects whose only special property is that they have no method that changes them. Copying as an abstract operation is totally meaningless, though wide classes of objects can define what it means for their instances---and always explicit, never done simply because you name something,  call a function, or do something equally irrelevant. And finally, when your son buys eggs in the shop, a completely realistic scenario happens. Yay!

2014-07-10

Which Python is this about?

There is only one Python. This might come as a surprise to many people, so let's explain.

When BDFL started to develop Python, he was influenced by many ideas from other programming languages. Despite what you might think if you tried lazily thinking about some sexy aspects of it that are nice to tinker about, language design is hard. Not even BDFL gets it right every time. But differently from most other language creators, he's not afraid to say when that happens, and fix things.

Example: a long time ago, Python had int and long, two different types to represent the same thing (mathematical integers), just because one was "native" (fixed size, directly supported by underlying architecture) and thus fast, and another was correct. Of course, it's known that in programming, most integers are used as indices in small and moderate size arrays, where native type surely suffices.

But that's solving the wrong problem. Having a type that, because of its name among other things, suggests representation of counting numbers (and their opposites), but is unable to represent the number of humans currently alive, is just wrong. C has two strong reasons for sticking to that point of view (bare metal approach, and value semantics), neither of which has anything to do with Python. Its types are supposed to be representations of real-life concepts, not of processor and memory induced abstractions in another programming language. [Of course, if one models a behavior of some C program in Python, int32 would be a perfectly appropriate type. But that's surely too narrow for a built-in.]

Guido realized that, and since Python 3, there is only one int type, which chooses internal representation according to its size, and the whole model is hidden from the user pretty well. From the perspective of a programmer, 2**3 and 2**99 is calling the same method, int.__pow__ (it's even the same bound method, 2 .__pow__:), and the result has the same type as the operands. Of course it required some very deep interventions in the implementation and documentation, but BDFL boldly did it. There were many other such huge changes (Unicode, true div, delayed functionals,...), which were treated just as boldly.

How could he be so bold? It's really simple: there is such a thing as ideal Python implementation. All real implementations are just shadows of that Platonic idea, and although we can't always intuit the real idea from the shadow a priori, the thing works really well a posteriori: once you see the true thing, it's obvious.

So, Python 3.4 (or whatever it is in the moment you read this) is the most accurate existing approximation of true Python, which is the real subject of this blog. People usually ask me why do I hate Python2... I don't hate it. The same way I don't hate Newtonian mechanics -- it's wonderfully useful, up to a few warts here and there. But it isn't deeply, really the way the Universe works. Of course, neither is General Relativity (and neither is Python3) -- it's just that it's the best thing we've got. And it's pretty damn good.

2014-05-08

What's this all about?

I am not so special. I have just decided to write this since I see so many people getting it terribly wrong. Yes, I know a lot about Python, but what enables me to write authoritatively here is that I understand Python. Why? Mostly because I have read George Orwell. Language is important, because it shapes our thoughts. Language deeply affects the way we think.

Today, probably as a consequence of a proliferation of a great multitude of languages, too often people think that all languages are the same - we just have to learn the differences in vocabulary. printf in C, cout in C++, ? in BASIC, writeln in Pascal, System.out.println in Java, WRITE in FORTRAN, DISPLAY in COBOL, document.write in JavaScript, . in FORTH,  in APL, putStrLn in Haskell, write in Prolog, SELECT in SQL, content: in CSS, \write in LaTeX - they are all the same, right? Of course, there are syntactic differences - cout must be followed by that weird <<, WRITE has that strange (*,*) after it, and  is not found on a normal keyboard, for example - but that's all. Just superficial differences. No?

[Note: yes, I really did put SQL and CSS in there. On purpose. I hope you realize that CSS content: is not the same as FORTRAN WRITE. Well, guess what: neither is Python print. If you treat them the same, you're missing the point. And the fact that CSS "isn't a programming language" doesn't really matter. If you know that difference, and still fail to see the others, that's even sadder.]

If we want to learn a language, learning only the words and grammar is really missing the point. Words and grammar rules are just consequences of something much more fundamental, the culture from which the language emerges and which it feeds. And it really shows in a language. It cannot be hidden, and you shouldn't want to hide it, anyway. I'm sure you know at least some of very good reasons why you need to type 1 character in BASIC and 18 characters in Java to do similar things. Without them, BASIC wouldn't be BASIC, and Java certainly wouldn't be Java.

Here, I'll try to show you that the language matters. You shouldn't write C in Python, because it sounds just as bad as Chingrish. Native speakers will understand you, but will also - despite good manners - find what you speak really facepalm-worthy.

Graham in trouble

You know about Paul Graham. If you don't, please read some of his stuff. His writings are really thought-provoking. One of his best pieces, IMO, is Beating the Averages. There, he writes (emphasis mine):
to explain this point I'm going to use a hypothetical language called Blub. Blub falls right in the middle of the abstractness continuum. [...] 
As long as our hypothetical Blub programmer is looking down the power continuum, he knows he's looking down. Languages less powerful than Blub are obviously less powerful, because they're missing some feature he's used to. But when our hypothetical Blub programmer looks in the other direction, up the power continuum, he doesn't realize he's looking up. What he sees are merely weird languages. He probably considers them about equivalent in power to Blub, but with all this other hairy stuff thrown in as well. Blub is good enough for him, because he thinks in Blub.
He is, of course, completely correct, as he is every time he writes about general things. The power of his mind to grasp truly general concepts and put them in simple words is astonishing. But now look at this (from Re: Revenge of the Nerds, again emphasis mine):
 I know that Python currently imposes these restrictions. What I'm asking is what these restrictions buy you. How does it make Python a better language if you can't change the value of a variable from an outer scope, or put more than one expression in a lambda? What does it buy you to distinguish between expressions and statements?
Later, he seems even more desperate:
I was actually surprised at how badly Python did. I had never realized, for example, that a Python lambda-expression couldn't contain the same things as a named function, or that variables from enclosing scopes are visible but not modifiable. Neither Lisp nor Perl nor Smalltalk nor Javascript impose either restriction. I can't see what advantage either restriction brings you.
Of course you can't see it, Paul. Of course you can't. Just read Beating the Averages again, and everything will be clear. Yes, the day has come. Now you're the Blub programmer. It's really kinda funny that you don't see it. Your "I was actually surprised at how badly Python did" clearly shows you don't get it. And your pleas "what does it buy you" fit your description "about equivalent in power to Blub, but with all this other hairy stuff thrown as well" so perfectly.

I'll explain all these things. On this blog, we will first unlearn every wrong concept from thorny history of programming, and then we'll see how programming should be done. In a language whose concepts are powerful enough to confuse even Paul Graham.