10 comments

[ 0.24 ms ] story [ 31.8 ms ] thread
Symbols are a foot gun.

> Symbols aren’t interchangeable though.

    user = { name: "Alice", age: 30 }
    puts user[:name] # Alice
    puts user["name"] # nil

I'm 100% convinced that every Ruby developer has at least once made a bug where they tried to access a hash entry using a symbol, where the key was actually a string or vice-versa.

It would be great if Ruby would finally have immutable strings by default and, at that point, it would be possible to make symbols be strings. This would prevent any such user[:name] vs user["name"] bugs while not breaking any other functionality. And also keeping the memory "optimized" by reusing a single immutable string.

OP is really tying themselves into knots.

Symbols are special cased pseudo-strings for langages which have either extremely inefficient strings (erlang) or mutable ones (ruby). That’s about the extent of it.

Python or Java don’t really have a use for them because their strings are immutable, and likely interned when metaprogramming is relevant (e.g. method or attribute names).

Lisps have unnterned symbols also.

Interning is important for symbols that are involved in I/O: being printed and read, so that two or more occurrences of the same symbol in print will all read to the same object, and so there is print-read consistency: we can print an interned symbol, and then read the printed symbol to obtain the same object.

Symbols are useful without this also. Symbolic processing that doesn't round trip symbols to a printed notation and back doesn't require interned symbols.

Symbols have a name, but are not that name.

They also have various properties, which depends on the Lisp dialect.

Classical Lisp dialects, like MacCarthy's original, endow each symbol with a property list. Another classical property is the "value cell".

In Common Lisp lists have a home package retrieved by the function symbol-package.

A variable being globally proclaimed special can be implemented as a property of the symbol.

Symbols are interned in packages, not globally, so two symbols can be interned, yet have the same name: mypackage:let and cl:let both have the name "LET", but different home packages.

Uninterned symbols with the same name can be readily made: just call (make-symbol "FOO") twice and you get two symbols named "FOO", which print as #:FOO.

The #: notation means symbol with no home package, used as a proxy for "uninterned", though a perverse situation can be contrived whereby a symbol has no home package (and so prints with the #: notation), yet is interned into a package.

Introduce a FOO symbol in the keyword package:

  [1]> :foo
  :FOO
Now import it into the CL-USER package:

  [2]> (import :foo :cl-user)
  T
Verify that cl-user::foo is actually the keyword symbol :foo:

  [3]> 'cl-user::foo
  :FOO
Now, unintern :foo from the keyword package, leaving it homeless:

  [4]> (unintern :foo :keyword)
  T
Let's print it, accessing it via the cl-user package where it is still interned by import:

  [5]> 'cl-user::foo
  #:FOO
There is quite a bit to this symbol stuff than just "interned strings".

Symbols are simply not strings objects; they have strings as a name.

I think the whole article is super confusing.

A Symbol is really just a string!

Well, it's a string that will guarantee unique allocations (two identical strings are guaranteed to be allocated at the same address), which makes equality checks super fast (compare pointers directly). But pretty much just a string nonetheless...

> Dang it, Ruby. You never cease to amaze me!

This has been true forever.

The article is a bit confusing, but I like the concept behind symbols: Basically a way to distinguish identifier names (chosen by the programmer) from user-facing strings.

The distinction between is one I've mentally adopted in other languages like Python as well. For personal projects I like to use single quotes for 'symbols' and double quotes for "strings", e.g.:

    add_label(text="Hello World!", align='center')
Does anyone else do something similar?
I have been with you for a very long time,until I recently realized how tricky the edge cases are, I cannot figure out a good rule.

A json api, should it use symbols or strings?

Json has string keys, but the api keys are chosen by the programmer.

I can't remember which edgecase,but there are another bunch I found that are tricky.

I'm a ruby developer, would never dream of switching to another language as my bread and butter - but my language design hot take for ruby is that symbols were a mistake and unnecessary and the language would have been better off just using frozen (immutable) string literals for everything except for the syntax of keyword arguments.

Unfortunately we can't change it now, but generally I just don't use symbols anymore unless I absolutely have to.

I still wish the Rails enums were written with slightly more object portability.