[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

orthogonality and generalized references (was Re: Zen of Python)



Avi Bryant writes:
> [in Ruby]
> dict['foo'] ||= 42
> [in Smalltalk, "extremely regular and explicit"]
> dict at: 'foo' ifAbsentPut: [42].
> ...
> What's the equivalent Python idiom?  My guess is that it will be either
> less regular, or less explicit, or both.  I'm not trying to pick on
> Python, I'm just trying to point out that there is no simple relationship
> between regularity, explicitness, and succinctness; it's entirely possible
> to have all three.

Python uses roughly the Smalltalk approach, although (like Perl) it
doesn't provide a convenient way for you to avoid evaluating the
default value in the case that the value is already present:
    dict.setdefault('foo', 42)

I don't quite understand what you mean by "regular" or "explicit"; the
Ruby/Perl idiom is less explicit, I agree, but it appears to me to be
more regular --- the Smalltalk/Python idiom here is to use a
special-purpose method, while the Ruby/Perl idiom is a combination of
three orthogonal language features that have nothing to do with
dictionaries or hashes: the boolean 'or' operator's return value, the
false result of looking up a nonexistent index in a dictionary or
hash, and the augmented assignment operators.  As a result, you can
also say (in Perl):

$x ||= 42;    # ordinary variable
$y[3] ||= 42; # list reference

Common Lisp has the notion of a "generalized reference", such as
(gethash "foo" dict), which can be set with setf or read by
evaluation; and it has boundp and makunbound, but they only work on
symbols, not generalized references.  In a CL dialect where boundp and
makunbound (somehow --- they standardly evaluate their arguments, so
(boundp (gethash "bar" x)) will tell you whether the symbol (gethash
"bar" x) returns is bound or not) worked on generalized references,
you could write a setdefault macro that would allow you to say things
like this:

(setdefault (gethash "foo" dict) 42)
(setdefault x 42)
(setdefault (nth 3 y) 42)

giving you the same level of regularity and orthogonality you have in
Perl and Ruby, along with the same level of explicitness you have in
Python (and, arguably, Smalltalk, although I think at:ifAbsentPut: is
more explicit than setdefault).

(I don't know Common Lisp; apologies for any mistakes.)

I wish I had a language where I could define this "setdefault"
operation; it requires Common Lisp's ability to define new operations
on generalized references (for example, with macros; you can do it
crudely in C++ with a reference or in Forth with a raw memory address)
along with Python's ability to get, set, undefine, or check
definedness of generalized references. [0] Is there such a language?


[0] Python conflates providing an initial value and replacing an
existing value with a new value, unfortunately, and provides 'check
definedness' by raising an exception when you try to get an undefined
value.  Also, Python's generalized references are variables, object
attributes (x.y), and indexing expressions (x[y]), not general
function calls; this sounds more limiting than it is.

-- 
/* By Kragen Sitaker, http://pobox.com/~kragen/puzzle2.html */
char a[99]="  KJ",d[999][16];main(){int s=socket(2,1,0),n=0,z,l,i;*(short*)a=2;
if(!bind(s,a,16))for(;;){z=16;if((l=recvfrom(s,a,99,0,d[n],&z))>0){for(i=0;i<n;
i++){z=(memcmp(d[i],d[n],8))?z:0;while(sendto(s,a,l,0,d[i],16)&0);}z?n++:0;}}}