Values and objects

Steven D'Aprano steve+comp.lang.python at pearwood.info
Sat May 10 02:15:20 EDT 2014


On Sat, 10 May 2014 12:33:28 +1000, Chris Angelico wrote:

> On Sat, May 10, 2014 at 12:19 PM, Rustom Mody <rustompmody at gmail.com>
> wrote:
>> For me, Marko's comment that variables in python are not first-class
>> whereas in C they are is for me the most important distinction Ive seen
>> (in a long time of seeing these discussions).
>>
>>
> https://en.wikipedia.org/wiki/First-class_citizen
> 
> For variables in C to be considered first-class, they must, by most
> definitions, be able to be passed around as parameters and return
> values. Some definitions also require that they be able to be
> constructed at run-time. How do C variables shape up?
> 
> 1) Passing them as parameters. You can pass a pointer to a variable,
> which is effectively the same as passing a variable to a function. 

No it is not. It is nothing like passing a variable to a function. You 
are passing a pointer, which is itself a value. True, it is a value which 
you, the author, gives meaning as a pointer to a variable, but that need 
not be the case. It might be a pointer to a part of an array, or a 
record, or to some random address in memory, or a dangling pointer. C 
allows you to perform arithmetic on pointers, which means you can 
construct pointers to nothing in particular.

In C, pointers are first-class values, since you can:

    - construct pointers at runtime;
    - pass them to functions;
    - return them from functions;
    - assign them to variables;
    - stuff them in structs or arrays.

But variables are not. You cannot create new variables, nor treat 
variables themselves as function arguments, or return them from 
functions. The closest you can do is *manually* use a pointer to a 
variable to simulate the same effect, but this is both too much and too 
little:

    - too much, because you aren't limited to passing pointers 
      to a variable, you can pass pointers to anything you 
      like, or nothing at all;

    - too little, because you are responsible for managing the 
      pointers, instead of having the compiler do so.

In Pascal, like C, you can't create new variables, or return them from 
functions, or embed them in records. But unlike C, you can pass them to 
functions, using a var parameter to use pass-by-reference. Algol is 
similar, except pass-by-name instead. I guess that makes Pascal variables 
second-and-a-half class values. Given:

procedure foo(var inside: integer);
  begin
    {...}
  end;

begin
  foo(outside);
end.

the global variable "outside" is passed to foo where it effectively 
becomes the local variable "inside". Notice that we don't manually have 
to concern ourselves with pointers, in fact whether Pascal uses pointers 
to implement this or not is entirely invisible to the coder using the 
feature. It could be using magic fairy dust for all we know. We simply 
write the most direct, natural code we possibly can:

foo(outside);

and the compiler does whatever magic is needed to ensure the variable 
outside, rather than the value of outside, is passed to the procedure foo.

There is nothing like that in C. You can only manually simulate it by 
passing a pointer to a variable, not by passing the variable itself. To 
argue that C pointers are "pass by reference" is like arguing that C has 
a garbage collector, because you can write one yourself.


> The
> callee can mutate your variable through the pointer. 2) Returning them.
> This is a lot more dodgy, owing to the dangling-pointer issue, but as
> long as you accept that the reference to a variable doesn't ensure its
> continued life, I suppose this might be acceptable. Maybe. But it's
> pushing it. 3) Constructing at run-time. Really REALLY pushing it. You
> can malloc and call that a "variable", but it's not a variable any more,
> it's just a patch of memory. In fact, all this proves is that variables
> represent patches of memory, and patches of memory are first-class.

It proves that pointers are first class (well, duh!). The C compiler 
makes very little effort to ensure that pointers actually point to 
anything. Nor can you create patches of memory ("I'll like an extra 
couple of terrabytes please"), or return them from functions, or insert 
them in arrays.

Rather than *creating* patches of memory, malloc merely allocates it from 
pre-existing memory.


> Not liking the results here. You might just as well define that all
> Python variables must be backed by a dictionary (it's just as true as
> "all C variables must be backed by memory") and then define the
> first-class-variable as a combination of a dict and a key.

Python variables aren't first-class either, but in fact we can get a bit 
closer to first-class than either C or Pascal.

Creating new variables is trivial. Since they don't need to be declared, 
you create a new variable just by assigning to it:

try:
    spam
except NameError:
    spam = 23


There are at least two other ways:

globals()['spam'] = 23
exec('spam = 23')


You can also delete variables:

del spam


Now this really shows first-class-ness. We don't have to give the name of 
the variable as a string, as in del("spam"), we just use the variable's 
name.

But there's no way to pass variables to functions, embed them in lists, 
etc. The closest we can do is pass a string, the name of the variable, 
and a namespace:

foo('spam', namespace)

To be truly first class, we ought to be able to write something like this:

x = 23
alist = [1, 2, x]
alist[2] = 42
assert x == 42


but we cannot.


-- 
Steven D'Aprano
http://import-that.dreamwidth.org/



More information about the Python-list mailing list