1

Had to learn javascript for a project, do not miss it one bit! (Though I recognize its use and scale of its impact, not only in the desktop, but server and mobile spaces likewise). One of the things repeated in a lot of literature is "Do not pollute the global namespace" and a plethora of reasonable reasons.

From what I understand, scheme is lisp-1 with reason, like this, and more.

  1. What is the convention on global variables in Scheme?
  2. Are there any pitfalls to be aware of in using global vs local variables? (especially those that don't throw errors per se but can cause unseen bugs).
Community
  • 1
  • 1
usernvk
  • 77
  • 1
  • 8
  • @[Will Ness](http://stackoverflow.com/users/849891/will-ness) ha ha forget the nay-sayers? What is your experience? I _really_ don't want to bug my code from the onset. – usernvk Apr 29 '13 at 18:28
  • 4
    @WillNess: The `*foo*` convention is not for globals, it's for special variables, since a special declaration can be disastrous for code if the name is used in what looks like a local lexical scope somewhere. Some Schemers use it too for globals with a specific status like customization globals. – Eli Barzilay Apr 30 '13 at 06:04
  • 1
    @[WillNess](http://stackoverflow.com/users/849891/will-ness) `*chuckles at self*` feel like a sheep for not getting it at first. `*earmuffs*` ?? Ever used it? Definitely very unlikely to cause conflict and break things. – usernvk Apr 30 '13 at 13:13

2 Answers2

3

For your first question -- there is no single convention for globals in Scheme. Some people use *foo* (for a reason that is different than the same naming convention in CL, as I said in the above comment), some use CAPITALS (in case-sensitive Schemes), but neither is popular enough to be considered a convention.

The second question is more subtle. Yes, there are pitfalls for having a single global namespace. These pitfalls are the same as the ones in Javascript and as in Common Lisp: it's the fact that loading random code can change the meaning of other code -- so if I want to load two libraries, and both define some foo global function, then one of these libraries would break the other. This is not a new problem, and there are various ways to deal with it.

The most obvious thing to do is to avoid using globals that are not part of your public interface. So, for example, instead of

(define helper ...)
(define foo ...)

you write

(define foo
  (let ()
    (define helper ...)
    (define foo ...)
    foo))

and if you have multiple functions in your interface you do something similar (eg, with Racket's define-values or using a list of functions). Note that this has a double protection: internal things like helper don't pollute the global namespace so it won't break other code -- and other code that defines a helper won't break this code. This includes foo too, in case it calls itself recursively.

A related hack is to write such code when you want it to be fast: with the first version, a Scheme compiler cannot compile foo into a fast loop since the binding can be mutated later on, but with the second version such optimizations are possible.

Another form of defensive programming when you don't have a module system is to grab values that are important for your code. For example, you can do this:

(define foo
  (let ([+ +] [- -] [* *] [/ /] ... more ...)
    (define helper ...)
    (define foo ...)
    foo))

and now the code has its own immutable bindings to the arithmetic operations, which protects it from changes to these operations in the future and allows safe optimizations of arithmetics in this code.

All of this is kind of a poor man's module-system-by-convention, which is similar to the common use of (function() { ... })() in Javascript. But of course, a proper module system makes this much more convenient and well behaved. Both newer Javascript versions and newer Scheme versions face this problem with a module system of some sort, and in Scheme the trend is to use only modules and avoid load as the unreliable hack that it is.

(And since I mentioned Common Lisp: you can see that it has the same problem since you might be overwriting global variables or functions from other files. The way to address that is with CL's package system, which can act as a kind of a weak module system. The reason it's weak is that it gives you the convenience of separate namespaces, but the namespaces are still global which means that optimizations are still very hard. In other words, these namespaces do not give you the "closed world assumption" that conventional module systems have, which means that the compiler still cannot assume that bindings will not change.)

Eli Barzilay
  • 29,301
  • 3
  • 67
  • 110
  • @[EliBarzilay](http://stackoverflow.com/users/128595/eli-barzilay) I expect when I'm done with [Simply](http://www.eecs.berkeley.edu/~bh/ss-toc2.html) [Scheme](http://www.eecs.berkeley.edu/~bh/downloads/simply/) and [The little Schemer](http://www.ccs.neu.edu/home/matthias/BTLS/) I'll understand more but from what I did, some code I saw two days ago `(let ([+ +] [- -] [* *] [/ /]....` makes sense now. The details on the pitfalls and workarounds, Simply Invaluable! – usernvk Apr 30 '13 at 13:06
2

You can have 'lisp-2' in Scheme; just preference all of your functions with a consistent set of characters. You could start by rebinding the functions defined in RnRS. Like this:

(define this-is-a-scheme-function-+ +)
(define this-is-a-scheme-function-call-with-current-continuation call-with-current-continuation)

However, by convention,

(define tiasf-call/cc this-is-a-scheme-function-call-with-current-continuation)
GoZoner
  • 67,920
  • 20
  • 95
  • 145
  • Thanks! More than answered ^^ Also, have no clue what you just said, maybe in a few more months when I'm done with _introductions_ to scheme. +1 info is gold. – usernvk Apr 30 '13 at 05:07