That's not what lexical scope is for

In one of Joe Marshall's posts on ways to accidentally break an abstraction, there are several top-level definitions which close over a lexical variable, like this:

(define lib-mapcar 
  (let ((calls-to-f 0))
    (add-counter! 'calls-to-f (lambda () calls-to-f))
    (lambda (f list)
      ...)))

I know this isn't supposed to be an example of good style, but I wince to see functions defined this way. lib-mapcar's argument list is several lines away from its name, hidden behind some unrelated profiling code and a lambda. This sort of thing is distressingly common in Scheme, and is even presented as good style sometimes. The usual justification is to keep the variable private, so you don't have to worry about name collisions or unexpected dependencies. But the cost in clarity far exceeds the tiny benefit in privacy.

I guess this is what happens when you don't have (or aren't accustomed to having) a module system. You start to think of top-level defines as "globals", to be avoided whenever possible, and look for some alternative. Some languages provide convenient alternatives, such as C's static (in all three of its senses), but in Scheme the only obvious alternative is to abuse lexical scope. And since everyone knows lexical scope is an unequivocal Good Thing, it's easy to overlook how awkward it is as an access-control tool.

3 comments:

  1. The impression that I got from Joe's post was that it was an example of something that you could no; but not an endorsement of the practice.

    Seeing that R6RS has a module system; there seems to be a consensus that modules system in Scheme are a commonly used feature.

    ReplyDelete
  2. Do you really think this is an abuse of lexical scoping?

    C's static is comparable to this use of lexical scoping (because C doesn't have closures).

    If your gripe is with the syntax used, you could easily rewrite this by defining a macro to let you put the argument list right next to the symbol name.

    I agree that using this approach would be very awkward for a large system. But, that's a different issue than what he is trying to illustrate.

    ReplyDelete
  3. I didn't mean to suggest that Joe thinks this is good style. However, some Schemers apparently do, since this isn't the first time I've seen it given as an example of what lexical scope can do. (IMO combinators make a much better positive example, and accidental collisions with e.g. MAP a better negative example.)

    Maybe "abuse" was too strong a word. The point of lexical scope is to make local variables effectively private by default. Using it to make static variables private doesn't conflict with that, but neither does it show why lexical scope is good. It's an inelegant application of an elegant feature.

    If it could be packaged in a macro with a suitable interface, I wouldn't have a problem with it. But I don't see any way (without having a module system, in which case why not use that instead?) to make a macro that doesn't disrupt the code that uses the private variable.

    ReplyDelete

It's OK to comment on old posts.