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

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

Ages ago I posted to this list about goal-directed evaluation in Icon.
This "ifAbsentPut" discussion will, I hope, allow me to express another
advantage of goal-directed evaluation.  In Icon, the ifAbsentPut idiom
is the following simple expression---Icon has only expressions, and not

/dict["foo"] := 42

To understand this statement, you must understand the unary '/' operator
and the notion of goal-directed evaluation.  The '/' operator computes
the l-value (if one exists, otherwise the r-value) of its operand IFF
the r-value is not null.  If the r-value is null, then the operation
fails.  With goal-direction, an operand failing causes its parent
operation to do whatever it is supposed to do when the operand fails.
(The rules for what to do are simple and regular, but I'm not going to
go into them here.)

In this case the parent is the assignment operator, which itself will
fail when its left operand fails.  If and when the right operand and
left operands have both succeeded, the assignment takes place and the
r-value is produced as a value.

So, the assignment above will assign the value 42 to dict["foo"] when
dict["foo"] is null and simply fail when dict["foo"] is non-null.  Note
that the l-value is computed only once.

The '/' operator can, of course, be applied to any expression.  Any Icon
programmer would recognize the idiomatic use of '/' above.  I find it
superior to the other schemes that I've seen.

I wish other languages exploited the fundamental difference between
success and failure that is essential to Icon.  It's awkward to code
your way around their absence in other languages.


-----Original Message-----
From: Kragen Sitaker [mailto:kragen@pobox.com] 
Sent: Monday, May 27, 2002 4:56 PM
To: ll1-discuss@ai.mit.edu
Subject: orthogonality and generalized references (was Re: Zen of

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