Skip to content

Instantly share code, notes, and snippets.

@cjfrisz
Created November 22, 2013 04:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cjfrisz/7594736 to your computer and use it in GitHub Desktop.
Save cjfrisz/7594736 to your computer and use it in GitHub Desktop.
The symbol 1
user=> (symbol "1")
1
user=> (number? (symbol "1"))
false
user=> (symbol? (symbol "1"))
true
user=> (= (symbol "1") 1)
false
user=> (= (symbol "1") '1)
false
@cemerick
Copy link

'1 isn't a symbol, it's still a number:

user=> (number? '1)
true

quote (a.k.a. ') prevents evaluation of the enclosed form, but that just means you're going to be left with that form, nothing more.

user=> (number? '42.8)
true
user=> (keyword? ':a)
true
user=> (vector? '[1 2 3])
true
user=> (list? '(1 2 3))
true

You might be confused because:

user=> (symbol? 'a)
true

…but that is so because 'a is actually (quote a); a is already a symbol, quote just prevents it from being evaluated within the current context.

FWIW, if you're doing e.g. symbolic manipulation of some sort, 1 is a perfectly acceptable "symbol" for the number 1; even better, since it actually is the number 1! :-)

@cgswords
Copy link

(I'm torn between laughing at someone explaining how quote works to Chris and discussing why cemerick's answer doesn't address the underlying philosophical problem that Chris is trying to bring up.)

If there is basic type tagging in Clojure (which is common in lisps), then numbers should have a different type tag than symbols, which means that (symbol "1") should be a symbol build off of the unicode/ascii representation of 1. Thankfully, Clojure does this, but not without being horrifically misleading. Consider this in Clojure:

user=> (symbol "1")
1
user=> '1
1

If we try this in another lisp (like Petite), we get a (more explanatory) result:

> (string->symbol "1")
\x31;
> '1
1

Similarly, if we try this in Racket, we get another result indicating the difference.

> (string->symbol "1")
'|1|
> '1
1

I'm with CJ Frisz on this: that's some serious shenanigans. The pretty-printer, while trying to be "nice", is actually being incredibly opaque here. By pretty-printing that not-quite-one as a one, it's unclear that the type tag is unexpected. And in any case, why doesn't string->symbol recognize that it's just a number and return the expected, correctly-tagged version?

@cemerick
Copy link

Well, it's a public exchange on a Clojure FAQ (at least, as much as FAQs exist re: the details of read and eval semantics), so I write so that as many people can understand as possible. Seems like a good salve to twitter convos. Of course, no offense intended.

I'll quickly concede that it'd be nice if Clojure printed ambiguous symbols with pipes, something that's come up as a potential language enhancement a couple of times. It's never risen to the level of must-have. shrug

Anyway, not sure what you mean by "recognize that it's just a number and return the correctly-tagged version". string->symbol, like symbol in Clojure, should return a symbol all the time, regardless of the contents you provide it, no?

@cjfrisz
Copy link
Author

cjfrisz commented Nov 23, 2013

I've spent about a day thinking about this, and I can't come up with a definitive answer.

I think it might be material to the discussion that Clojure symbols are much heavier-weight than Scheme/Racket symbols. Under the covers, each Clojure symbol is a distinct Java object with a name string and (optionally) a namespace string. On the other hand, the Scheme standard specifies that all instances of a symbol with a given name be exactly the same symbol. To illustrate:

user=> (= 'a 'a)  ; value equality
true
user=> (identical? 'a 'a)  ; pointer equality
false
> (eqv? 'a 'a)  ; value equality
#t
> (eq? 'a 'a)  ; pointer equality
#t

As an aside, this might make Clojure symbols look crazy from a Schemer's point of view, but it's important to note that the notion of symbols in Scheme maps more closely onto Clojure keywords. For instance:

user=> (= :a :a)
true
user=> (identical? :a :a)
true

In the end, I think the answer is either that the Clojure way of printing symbols is entangled with the underlying representation, which from what I can tell is necessary for avoiding some of the pitfalls of not having a hygienic macro system, or else Rich Hickey didn't think direct usage of symbols was common or important enough (as in Scheme/Racket) to warrant extra work for sanitizing a leading number. Perhaps this tweet indicates the latter...

@cemerick
Copy link

I think you have it just right, @cjfrisz. The only quibble I'd have is that if "heavyweight" was meant to refer to the relative size of each keyword / symbol, then keywords are actually the weightier of the two; each clojure.lang.Keyword instance contains a corresponding symbol, plus some hash stuffs. Of course, the interning of keywords you mention means that they might be faster to obtain if they're already in the pool.

@cgswords
Copy link

(Cemerick, that wasn't meant as a slight against you, because I've seen a lot of users who need the exact explanation you just gave. I found the situation genuinely amusing, not offensive.)

I agree with @cjfrisz that, given Clojure considers itself a Lisp, it seems unexpected that symbols act so differently than in most other modern Lisps (from clisp to racket), and I think this is a pairing of printer ambiguity (which may be hectic to fix) and unexpected symbol behavior.

Is this a bug or feature?

FWIW, symbols are interned in most Lisp dialects I've used.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment