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

various recent ll1 threads



I thought I'd reply to recent LL1 traffic with one message instead of
several.

Paul Prescod writes:
> If succincness=power, COBOL would be not just be a dying programming
> language but a failed *idea*. The very idea of trying to make a
> programming language "like English" would be ridiculous. Clearly at
> least some language designers disagree that succinctness is an
> overriding goal. COBOL was supposed to bring programs to the
> programmer's mental model rather than vice versa. (I don't think it
> succeeded, but I'm glad someone made the attempt)

I think COBOL was supposed to make programs comprehensible without
special training on the part of the reader, and I believe it
succeeded.  I have heard from consultants who worked with COBOL and
non-COBOL shops that non-programmers in the COBOL shops regularly read
and understood source code, an event that was rare in non-COBOL shops.

The principle Paul Prescod puts forth is a related one: any Python
programmer should be able to read and understand any Python program,
even in a problem domain they don't know anything about:

> When I write code I always have in the back of my mind whether an
> occasional Python programmer could follow it. I will use an advanced
> feature if it dramatically simplifies the code. Otherwise, I will
> avoid it.

On infix versus prefix:

> That presumes that infix notation is merely a whimsical preference that
> could change soon whereas I think that it probably evolved over millenia
> as the best solution to keeping operators close to their operands.

That's what I thought, too, but then I examined some expressions.

In ((2 - 3) * (4 + 1)) / (11 + 3), the / is separated from its child
operators (* and the second +) by six and two tokens respectively, for
a sum of eight.  * is separated from its child operators by two and
two tokens, for a total of four.  The other three operators are
adjacent to their operands, so we have a total of only twelve
intervening tokens.

In the prefix notation / * - 2 3 + 4 1 + 11 3, / is separated from its
children by zero and seven tokens; * from its by zero and three; and
each of the +s and the - by zero and one, for a total of thirteen,
slightly more than in parenthesized infix notation.  If the order of
/'s children were swapped, prefix would keep the operators closer to
their operands than infix.

I think Dijkstra's comment that infix notation allows you to not
express order of operations when it doesn't matter is closer to the
mark, although with Lisp's variadic operators, that advantage seems
moot.


On binary operators versus functions; Paul Prescod wrote, with regard
to reduce:
> There are only a few binary infix operators (compared to the number of
> things in the universe that are already functions). So you can't get by
> without the for-loop (or recursion or similar alternatives).

Generally, pretty much any function whose domain is sets or lists of
some type can be expressed in terms of a reduction with a binary
function.  For example, you can express a curried 'map' in terms of
this binary function:

class mapper:
    def __init__(self, func):
        self.func = func
    def __call__(self, list, item):
        return list + [self.func(item)]

squares = reduce(mapper(lambda x: x * x), range(10), [])

(Python's 'reduce' works from left to right, like Haskell's foldl.)

Here's a Common Lisp version of the same that works from right to left:
* (defun mapper (fn) (lambda (item list) (cons (funcall fn item) list)))
MAPPER
* (reduce (mapper (lambda (x) (* x x))) '(1 2 3 4 5 6 7 8 9 10) :from-end t
	:initial-value '())
(1 4 9 16 25 36 49 64 81 100)

Likewise, you can express 'filter':

class filterer:
    def __init__(self, func):
        self.func = func
    def __call__(self, list, item):
        if self.func(item): return list + [item]
        else: return list

threes = reduce(filterer(lambda x: x % 3 == 0), range(20), [])


On syntactic abstraction in Python:

Paul Graham writes, quoting Paul Prescod:
> > You can do this, if you really need to, but you do it at runtime, not
> > compile time. As I said before, it simplifies the programmer's mental
> > model if they have to think only about runtime OR compile time, but not
> > both.
> 
> When you say you do it at runtime, do you mean you in effect
> call eval?  Because you can do that in Lisp too; it's just
> considered a hack.

Paul Prescod had said (quoting Peter Norvig) that you have eval and
operator overloading, so you don't miss macros all that much.  It's
true that Python's operator overloading lets you extend the language
in surprising ways; for example, one afternoon, I wrote a simple
symbolic math package that let you write algebraic expressions in
Python.  Here's an interactive Python session using it:

>>> import sym
>>> x, y = sym.vars.x, sym.vars.y
>>> myexpr = 3 * x * y + 2 * x - y + 13
>>> print myexpr
(((((3 * x) * y) + (2 * x)) - y) + 13)
>>> myexpr.where(x=3, y=3)
43
>>> myexpr.where(x=0, y=0)
13
>>> print myexpr.derivative(x).simplified()
((y * 3) + 2)


This was the 120 lines of code in
http://lists.canonical.org/pipermail/kragen-hacks/2000-December/000278.html
--- I posted it to comp.lang.python, and I think I actually hacked it
to be able to differentiate a bunch more things before I lost
interest, but I lost that version.

Since you can also "overload" attribute access, you can also do things
like this:

>>> import xml
>>> x = xml.xml
>>> x.p("This is some bold text: " + x.b("some text") + " and " + x.a("this is a hotlink", href="http://www.example.com";) + " and this is an image: " + x.img(src="http://www.example.com/dum.png";))
'<p>This is some bold text: <b>some text</b> and <a href="http://www.example.com";>this is a hotlink</a> and this is an image: <img src="http://www.example.com/dum.png"; /></p>'

That code is here:
http://lists.canonical.org/pipermail/kragen-hacks/2000-September/000266.html

So you can go some distance in the direction of syntactic abstraction
in Python with operator overloading.  But I often wish for more, for a
variety of reasons explained in _On Lisp_.  Most often, I wish for
unwind-protect macros (Scheme's with-open-file, for example, and
sometimes I wish for an analogous with-lock-held) and new binding
constructs, although I would like these to be merely a more convenient
interface to existing functions.

Perhaps I am too polluted with C++ --- the operator overloading tricks
above in Python also work in C++, and unwind-protect "macros" in C++
are simply auto objects with destructors.

Paul Graham writes:
> For example, a lot of Viaweb's competitors used languages like C++,
> presumably partly because they were "mainstream" languages and
> anyone who came along later to maintain them would have an easy time
> of it.  But this same technology made it slow to *develop* programs.
> Result: they are out of business, and no one is reading all their
> nice new-hire friendly mainstream code.

Didn't Viaweb have a substantial body of code in C or C++, even in the
early days?  I seem to recall that you were the only one of the three
founders whose first choice was Lisp.

-- 
<kragen@pobox.com>       Kragen Sitaker     <http://www.pobox.com/ragen/>
Silence may not be golden, but at least it's quiet.  Don't speak unless you
can improve the silence.  I have often regretted my speech, never my silence.
-- ancient philosopher Syrus (?) via Adam Rifkin, <adam@cs.caltech.edu>