Wednesday, December 1, 2010

Nulla Salus Extra Ecclesiam

There is always a well-known solution to every human problem - neat, plausible, and wrong. — H. L. Mencken
In software design, there often appears to be a situation where you either get a concept exactly right, or it will just be plain wrong and mess up your entire downstream development. (Maybe sometimes there are multiple good ways to solve a problem, instead of just one, but the wrong ones will always outnumber them.)

I think a good example is the difference in lexical scoping in Scheme vs Python. For my tastes, Scheme gets it exactly right, and harvests a lot of benefits from its clean design, whereas Python is just plain wrong, and harvests a lot of pain, everywhere. [In my limited experience.]

How can we avoid making systemic design errors? One way is to stick to what works. If you're designing a new PL, and your lexical scoping is different from Scheme or Haskell, I think you have a lot of explaining to do.

To know what actually works, you need taste. To create new designs without systemic error, you need a lot of midnight oil.

Which brings us back to:
Why do you glorify doing something new and stupid, when doing good things well is what people really should be admiring. — Linus Torvalds


Martyloo said...

Could you briefly explain why python lexical scoping is wrong?

Manuel J. Simoni said...

IIUC, assigning to a variable creates a local variable (modulo some arcane rules). So you need to demarcate global variables explicitly (which I find laborious and error-prone). This leads to the issue that you need to explicitly state which variables you want to close over in a lambda (laborious; error-prone(?)).

Anyway, fluent Pythonistas tell me they can work with it, some even like it, and people do get awesome stuff done using Python. When I say Python does it wrong I'm speaking from my own whimsical language design ivory tower.

Basu said...

My professor is a big fan of Python. He explains functions to students as "closed worlds" which is a pretty good description. But once you've used a language with proper lexical closures, Python does seem rather arcane.

Wyatt said...

In Python, you only need to mark a variable as global in a closure if it is shadowed in the closure (which can be a function created by def or lambda).

Anonymous said...

In Python, assigning to a variable creates a local variable unless that name has been declared 'global' or (in 3.x) 'nonlocal'.

You do not need to explicitly state which variables you want to close over in a lambda.

Tommy McGuire said...

From the Python docs:

Although scopes are determined statically, they are used dynamically. At any time during execution, there are at least three nested scopes whose namespaces are directly accessible:

* the innermost scope, which is searched first, contains the local names
* the scopes of any enclosing functions, which are searched starting with the nearest enclosing scope, contains non-local, but also non-global names
* the next-to-last scope contains the current module’s global names
* the outermost scope (searched last) is the namespace containing built-in names

If a name is declared global, then all references and assignments go directly to the middle scope containing the module’s global names. Otherwise, all variables found outside of the innermost scope are read-only (an attempt to write to such a variable will simply create a new local variable in the innermost scope, leaving the identically named outer variable unchanged).

Back in the pre-2.0 versions, Python did not have the "enclosing function" scope, so you would have to use a parameter x with a default value of x to bring the variable x from the enclosing scope into an inner function.

But fundamentally, the assignment-is-introduction thing is pretty seriously broken, with a variable getting global or enclosing scope until you assign to it, then being local.