Arc Forumnew | comments | leaders | submitlogin
3 points by waterhouse 3747 days ago | link | parent

The way ac.scm is currently implemented, it doesn't even matter if you rebind the symbol nil:

  arc> (mz (set! _nil 'ach))
  arc> nil
This is because nils are turned directly into 'nils:

  (define (ac s env) ;ac.scm
    (cond ((string? s) (ac-string s env))
          ((literal? s) s)
          ((eqv? s 'nil) (list 'quote 'nil))
Regarding numbers (and strings, you mention later): Well, why not allow '( to be a symbol as well? Because parentheses are special characters, and the reader interprets them as such. If you really want to make a symbol using special characters that the reader will recognize, you can escape the characters, using backslashes or vertical bars:

  arc> (= i\'m-a-symbol 2)
  arc> (+ |i'm-a-symbol| 3)
  arc> (list 'bob\ the\ hun '\( '\ )
  (|bob the hun| |(| | |)
Numbers are special characters, in this way: if an entire token consists of numbers (or "<numbers>.<numbers>", and various other number formats), then the reader treats it as a literal number. But you can escape the numerical characters and bind the symbol 2 if you want:

  arc> (= \2 3)
  arc> (+ \2 4)
  arc> '\2
Escaping non-special characters seems generally to do nothing:

  arc> (is 'ach '\a\c\h '|ach|)
So, really, you can already bind the symbols 2, "meh", and so on. It's just that you need to escape them, or else the reader will interpret them as literal numbers and strings and so forth. (By the way, the Arc compiler still interprets the characters ~.&: as ssyntax even if you escape them. That is most likely a bug, rather than a conscious design decision.)

In light of the above, and aw's example of the expression (let nil 3 ...) vs (let () 3 ...), it seems like either the reader is treating nil as a special case and turning it into (), or the whole macro-expansion environment is treating the symbol 'nil specially (not the value of the symbol nil, but the symbol 'nil itself). In fact, we can see in ac.scm that the latter is the case. Either way, handling of 'nil happens before the value of the symbol even starts to matter. (And hey, that's basically what I said at the top!) It seems that if you want (= nil something-else) or (let nil something-else) to have any noticeable effect, then you can't have (let () ...) mean the same thing as (let nil ...) anymore. I don't like that option. I like having nil be a literal empty list.

However, I could see this being done: have nil be escapable, so that |nil| is the symbol you can rebind, while nil is the literal empty list. ...And then are they eq? If they were, then macros passing around |nil| would probably blithely turn it into nil. If not, then... then this official nil symbol is not the same thing as a symbol constructed from the characters n,i,l, which breaks my mental model of the universe. So I guess being able to say (let nil ...) just affords nil special status. (I suppose 'let could very well treat the symbol 'bob specially as well--but no, because let is really skin on top of fn, which is a fundamental part of the language.) I think I'm happy requiring that nil be un-rebindable.

Regarding t: I am all for allowing local rebinding of t. It's already possible to (let t 2 t), just not to follow it with (++ t), which seems pretty clearly to be a bug. I also agree with allowing global rebinding of t; it really is just a symbol, and even though it does show up in the primitives ('is and 'atom choose to return 't as their truth value), they could just return 't rather than t (i.e. quote the symbol), and then t wouldn't have to have a value at all. It is simply for convenience that we effectively have (= t 't). So, yes, t should be rebindable, and I have accordingly modified my ac.scm; just note that if you do rebind t, then everything under the sun is liable to stop working, though less so than if you rebind car:

  arc> (= car nil)
  arc> (= a 2)
  Error: "Function call on inappropriate object nil ((a 2))"

2 points by evanrmurphy 3747 days ago | link

The escaping system is more robust than I realized. I think some of my concerns are definitely addressed with being able to assign escaped numerical characters, etc.

> However, I could see this being done: have nil be escapable, so that |nil| is the symbol you can rebind, while nil is the literal empty list. ...And then are they eq?

I looked into this and think it's kind of elegant now. 'nil is unescapable like non-special character symbols in general are [1]:

  arc> (is 'nil '\n\i\l '|nil|)    ; like your 'ach example
But () is escapable (as special characters typically are):

  arc> (= |()| "escaped empty set")
  "escaped empty set"
Like typical escaped characters, it isn't eq to nil and () unless you assign it that way:

  arc> (is () |()|)
  arc> (= |()| nil)
  arc> (is nil () |()| \(\))
[1] I was frustrated momentarily when I couldn't escape 't until I realized it was the same way. As you pointed out, at least it is locally rebindable by default. And then I suppose 't isn't an especially desirable global variable name anyway.