On 14 June 2010 06:56, Eli Barzilay
<eli@x> wrote:
The bottom line is that this:
(define (map f l)
(define (loop l)
(if (null? l) '() (cons (f (car l)) (loop (cdr l)))))
(if (list? l)
(loop l)
(error "poof!")))
can be proved as a correct implementation of map *assuming* no changes
to the list ever occur in the call to `f', or more generally during
the dynamic extent of the call to `map'. If you're one of these
theoretical people, there's not much else to see here.
The problem is on the side of the *working* programmer
Your "*working*" programmer is either made of straw or demonstrably incompetent in building semantically sound interfaces. If the list structure is part of the invariants in the interface, then there should be no other piece of code touching it. In short, *both* the programmer who elected to use MAP, and the programmer who mutated the list are wrong. Removing mutable pairs only solves the latter problem, which is, frankly, far less pernicious than the first - which should be soundly punished by giving the debugging task to the perpetrator. Like we do in industry All. The. Time.
This is a case of straining out a gnat and swallowing a camel.
given only mutable pairs, the only way to guarantee that your own
pair-related invariant (such as `list?'ness) is not broken is to copy
it:
(define (map f l)
(define (loop f l)
(if (null? l) '() (cons (f (car l)) (loop f (cdr l)))))
(if (list? l)
(loop f (loop values l))
(error "poof!")))
In almost two decades of reading and writing Scheme code, I have not
seen this done even once.
If this was *that* big of a problem, or worse - as big as the _goto_ problem - I think we'd have seen a larger reaction before this, hmmm? SET-CAR! and SET-CDR! and their predecessors RPLACA and RPLACD have been around a lot longer than two decades even.
To put this differently: Schemes (and Lispers)
have been dreaming for a really long time about Scheme/Lisp operating
systems and other large scale systems.
And, interestingly enough, they are still almost entirely written in C, assembly, and other *even less safe* languages. So what does that tell us, hmm?
Would you be happy to know that my API passes on your bank account as
a structure made of mutable pairs to these plugins?
If your API contains any interfaces *at all* that contain the ability to mutate my account information, you'd better have some serious security vetting in place for the consumers of your API - including serious acceptance testing cycles just like the banks (where i currently peddle my software) have.
The bottom line is that mutation is harder than it first looks.
The bottom line is that every example thus far has been more full of design flaws than an undergrad senior project. If you want Haskell, go program in it.
Bugs
involving state will almost always be found in some code that is
syntactically unrelated to the buggy code. I've spent too many nights
tracking such bugs to just let myself go with the "assuming no bad
mutation" flow and hope for the best.
My day job is about 80% debugging (read that as "reasoning about") code written in dialects of C by other people, accumulated over the course of 20 years, without documentation or even any institutional memory or its original provenance. I assume *nothing* because nearly every invariant has been smashed as this program evolved the *BIG* ball of mud pattern all on its own. Fortunately, I have almost a decent debugger available to me - and that is something which is seriously lacking in the Scheme world - even PLT's, which is arguably the best of the lot (using it was the reason I started this discussion in the first place), doesn't really get the job done when it comes to untangling conflicting assumptions.
It's a poor workman who doesn't know when to get better tools. And a better saw won't help you measure the right length before you cut. Mutable pairs is most emphatically *not* the problem here.
But *why* lump mutable data together with something as
fundamental as pairs? Where's the "jewel-like"ness of that?
Because Scheme allows mutability as a top-level entity without reference to a state monad. Its a foundational part of the design space.
If anything, this lumping *is* -- IMO -- an "attack on the core nature of
Scheme"
Haskell is over -> there.
IMO, features that can be separated into N sub-features should always
be separated
So are you ready to take on the notion that all functions should only have one argument and deal with the necessity of re-defining the procedure call semantics in a way that is syntactically consistent with legacy code, while supporting a proper decomposition of the Scheme calling sequence, thus making CALL-WITH-VALUES obsolete? No, I didn't think so.
So the whole dilemma (if I had one, that is) boils down to the
question of preserving legacy code.
Specifically, it boils down to the ability to bring in code which uses a-lists as a mutable lookup table. I can't do that with R6RS or PLT anymore without (admittedly minor) surgical alterations. Bye-bye PLT, you've become a useless debugging tool now.
Programming languages are like sharks. When they stop moving
forward, they die.
Puh-lease. Everybody has their own issues on which they want movement and those that want held firm.
Keeping legacy code as a holy grail means that (among other issues)
mutable pairs are going to stick around with Scheme until it dies.
Yes, I do think that was Brian's goal :)
david
--
GPG Public key at