[HN Gopher] Why Ruby Has Symbols
       ___________________________________________________________________
        
       Why Ruby Has Symbols
        
       Author : dmitrytsepelev
       Score  : 131 points
       Date   : 2022-04-14 07:52 UTC (15 hours ago)
        
 (HTM) web link (dmitrytsepelev.dev)
 (TXT) w3m dump (dmitrytsepelev.dev)
        
       | barrkel wrote:
       | Ruby doesn't have symbols because of AST or VM details.
       | 
       | Ruby has symbols in all probability because Lisp and Smalltalk
       | have symbols.
       | 
       | It could get most of the same practical upside of symbols from
       | interned strings - the important thing is being able to compare
       | using pointer equality and look up hash tables without needing to
       | walk a string. What symbols at the type level do is ensure that
       | these string-like things have already been interned, that is, de-
       | duplicated, when they hit lookup points like member access.
       | 
       | But the implementation could do something very similar behind the
       | scenes by setting a bit on interned string values. Besides,
       | symbols aren't enough for the more advanced dynamic language
       | optimization techniques like you see in V8.
        
         | thaumasiotes wrote:
         | > It could get most of the same practical upside of symbols
         | from interned strings
         | 
         | Aren't symbols and interned strings the same thing? Of course
         | you can get all the upside of symbols by having symbols...?
        
           | burke wrote:
           | Yeah--to reference a symbol `:foobar` in MRI's C API, you
           | even call `ID2SYM(rb_intern("foobar"))`
        
         | zimbatm wrote:
         | That's a better explanation. There is a clear Smalltalk
         | influence in Ruby, especially around the object-oriented
         | aspects of the language. The best example is how the language
         | doesn't call function, but sends a message to a method. And
         | also how _everything_ is an object. Matz talked quite a bit
         | about the various other languages that influenced the design
         | and Smalltalk and Lisp are part of that list (and Perl).
        
         | nine_k wrote:
         | I'd say that Ruby has symbols because Ruby has mutable strings.
         | 
         | If your strings are immutable and interned, they are as good as
         | symbols; this is why Python does not have symbols.
         | 
         | ECMASript introduced symbols because JavaScript strings, while
         | immutable, are not necessarily interned. Symbols are much
         | cheaper to compare for equality: you only need to compare the
         | pointers / ids, not actual string bytes.
         | 
         | Lisp has symbols for the same reason: Lisp strings are vectors,
         | which are also mutable.
        
           | RobertWHurst wrote:
           | At least in V8 they are last I checked. The symbols feature
           | is a property privacy feature. A symbol can be treated as a
           | private secret owned by a library thus restricting access to
           | a property on a shared object.
        
             | qubyte wrote:
             | This is untrue. Symbols provide no privacy. They provide a
             | mechanism to avoid collisions. You can even ask an object
             | for its symbol properties:
             | https://developer.mozilla.org/en-
             | US/docs/Web/JavaScript/Refe...
        
           | lispm wrote:
           | Lisp has symbols, because they were used in symbolic
           | expressions (s-expressions) as named entities. In the
           | programming language Lisp these symbols serve also as
           | identifiers for functions, variables and other things. Thus a
           | symbol originally had an internal structure made of an
           | association list (a list of keys and values). That
           | association list then had various entries, including a print
           | name -> the thing to print when a symbol gets externalized.
           | Since symbols can serve as function names, these symbols also
           | had functions stored in their association list. Different
           | function types could be stored under different keys.
           | 
           | Since Lisp symbols serve a central role as identifiers and
           | structured objects, they are not like what Ruby uses. Lisp
           | uses symbols also for named interned things, but that is only
           | one purpose.
           | 
           | In Common Lisp symbols have a name, a value, a function, a
           | package and a property list (a list of keys and their
           | values). By default in a call like (mult 1 2 3), the global
           | function will be retrieved from the symbol and the function
           | will be called with the arguments. The property list
           | sometimes will be used by an IDE to store information about
           | the symbol: like where it was defined, what its definition is
           | and similar.
        
         | the_mitsuhiko wrote:
         | I feel quite confiscate that Ruby has Symbols because Strings
         | are mutable which causes issues for when you hold on to
         | something but you also give out a reference.
        
         | jfmc wrote:
         | Prolog also have symbols (called "atoms"). Erlang too
         | (influenced by Prolog).
        
           | masklinn wrote:
           | Erlang has symbols because its strings are ridiculously
           | expensive (and kinda shit), so while it does have immutable
           | strings identifying objects based on that would be
           | ridiculously costly.
        
         | [deleted]
        
         | chrisseaton wrote:
         | > It could get most of the same practical upside of symbols
         | from interned strings
         | 
         | I don't think so - unless you mean _always interning all
         | strings_. The point of symbols is you can do a single address
         | comparison. How can you do that if you could have two strings
         | that are the same but have different addresses?
         | 
         | There is also a down-side of symbols - they by definition
         | always escape the compilation unit since they're interned!
        
           | samatman wrote:
           | Always interning all strings is what Lua does, and the
           | concept of a symbol in Lua is merely a particular string
           | pattern which the parser will recognize. They aren't
           | syntactically identical, you can replace any .field with
           | ["field"] but you can't say `local ["field"] = value`, but
           | there is no distinction in the types.
           | 
           | I get a lot of use out of both of those decisions (immutable
           | strings and string/symbol identity), they work well together,
           | and I'd (much) rather have the problem of string-builders
           | than the problem of tracking references to strings and
           | copying them if I need both the original and revision.
        
             | chrisseaton wrote:
             | > Always interning all strings is what Lua does
             | 
             | That'd be catastrophic for performance in Ruby - every
             | string allocation would always have to be reified, and
             | would always need to access a shared data structure.
        
               | samatman wrote:
               | I'll take your word for that. It's the opposite in Lua,
               | which is several times faster than stock Ruby. If your
               | runtime takes it as a given that every string will be
               | interned, there are all sorts of assumptions this enables
               | which mutation invalidates.
        
               | chrisseaton wrote:
               | > If your runtime takes it as a given that every string
               | will be interned, there are all sorts of assumptions this
               | enables which mutation invalidates.
               | 
               | Yes but if you're regularly creating strings, which is
               | what Ruby web servers do all the time, then your intern
               | table is going to become a white-hot hotspot, contended
               | by all threads all the time.
        
               | samatman wrote:
               | That's one way of stating the problem, sure.
               | 
               | It's more accurate to say that Ruby has mutable strings
               | as part of its semantics, and that can't be changed, and
               | would interact disastrously with immutability.
               | 
               | Languages with immutable strings simply architect a web
               | server around that semantic, the problem you're
               | describing doesn't happen. A mutable string is replaced
               | with (in Lua) a mutable array containing immutable string
               | fragments, which are made into a string using the builtin
               | table.concat.
        
               | chrisseaton wrote:
               | > A mutable string is replaced with (in Lua) a mutable
               | array containing immutable string fragments, which are
               | made into a string using the builtin table.concat.
               | 
               | That's what TruffleRuby does internally! We give you
               | 'mutable' Ruby strings, but really they're made up of
               | fragments of immutable strings.
        
               | samatman wrote:
               | So it.. isn't catastrophic for performance?
               | 
               | Edit: I'm guessing you're saying they're immutable but
               | not necessarily interned. Ok.
        
               | chrisseaton wrote:
               | Yeah - if _all_ strings are interned that 's ok for
               | comparison, but atrocious for string allocation. If not
               | all strings are interned that's better for allocation,
               | but atrocious for string comparison.
               | 
               | We already have the best solution... symbols. An
               | alternative set of semantics that work well with our
               | hardware.
        
               | spc476 wrote:
               | Always measure. I use Lua at $job, where it's processing
               | millions of SIP messages (text! Lots of parsing) in real
               | time (it's part of the call processing flow), and over
               | the past seven years or so, I've only had to tune the
               | code once in that time (maybe two, three years ago now?).
        
               | [deleted]
        
           | masklinn wrote:
           | > I don't think so - unless you mean always interning all
           | strings. The point of symbols is you can do a single address
           | comparison. How can you do that if you could have two strings
           | that are the same but have different addresses?
           | 
           | You intern all the literals (which includes lexical symbols)
           | and are 99% if the way there.
        
             | chrisseaton wrote:
             | > and are 99% if the way there
             | 
             | I don't understand - if you're not 100% of the way there
             | then you can't rely on address comparison. 1% of your
             | string comparisons would fail!
        
         | ravi-delia wrote:
         | Interned strings are fine if you don't have mutable strings,
         | but for one Ruby does have mutable strings and two it's nice
         | having that syntactic sugar! Makes it clear that some value is
         | something programmer-written, or at least programmer endorsed.
         | I don't use python much but I do wish there was an alternative
         | syntax for strings I only plan on using like symbols.
        
       | chrisseaton wrote:
       | My opinion is that Ruby has symbols for strings that are static -
       | part of the program - and normal strings for dynamic runtime
       | data.
       | 
       | Separating the two is useful semantically because it lets you
       | differentiate between the two - and because these two kinds of
       | string are better off being implemented and optimised in
       | different ways.
       | 
       | So it's both UX and practical mechanical sympathy.
        
       | agalunar wrote:
       | > When AST is built, it is validated to make sure it makes sense
       | (that's called lexing) and converted it to the bytecode.
       | 
       | I've never heard "lexing" used this way, and I believe it's
       | simply incorrect. Lexing (tokenizing) precedes parsing (parse
       | tree and then syntax tree construction). It isn't syntax tree
       | validation.
       | 
       | Or so I thought. Are there other examples (besides this article)
       | of "lexing" also being used to mean something else?
        
         | Existenceblinks wrote:
         | I think, it's called "elaboration" (In Standard ML) .. into
         | elaborative semantics (AST that makes sense for evaluation
         | phrase).
        
         | dmitrytsepelev wrote:
         | Thank you for catching that! Not sure where my eyes were, feels
         | like it's a remnant of another version of the sentence. Removed
        
           | agalunar wrote:
           | No problem! Same thing happens to me, where my eyes begin to
           | gloss over things I've written (because I know what I mean to
           | say).
           | 
           | Thanks for the submission!
        
             | carapace wrote:
             | BTW, a good technique for catching those kinds of mistakes
             | is to read the piece out loud. Engaging more of your
             | nervous system makes the visual elision easier to detect.
        
         | nine_k wrote:
         | > _validated to make sure it makes sense_
         | 
         | Indeed, I always thought that it's called "syntax checking" :)
        
       | rkangel wrote:
       | Elixir (and Erlang) have atoms which are exactly the same thing.
       | They're useful in any dynamic programming language - in a static
       | one, the equivalent is different values of an enum.
       | 
       | Python notably doesn't, and as such you get functions that take
       | arguments that are strings with special meaning, which I always
       | found a bit clunky even before I discovered Ruby.
        
         | tomn wrote:
         | I think Erlang has them for a more specific reason: in general
         | it eschews all forms of data definition (records are just a
         | fancy syntax for tuples with an atom at the start), which makes
         | hot code reloading and transparent network communication
         | simpler.
         | 
         | Both cases would require some synchronization of data
         | structures (across time or over the network), and with user-
         | defined types this can get complicated, and atoms make the lack
         | of user-defined types much more pleasant (and more performant
         | than strings).
        
         | quietbritishjim wrote:
         | Python has had enums [1] since Python 3.4 (2014). You can
         | easily convert back and forth between enum values and their
         | string and numeric values.
         | 
         | I don't know anything about Ruby or Erlang so I don't know if
         | that's really relevant, just your comment seems to imply it
         | doesn't.
         | 
         | [1] https://docs.python.org/3/library/enum.html
        
           | rkangel wrote:
           | Yes, that's fair. I suppose I was describing my process of
           | "discovery" and learning with Python which was significantly
           | before 2014. Even now though, enums are not usually the
           | normal way of doing things in public APIs, but that's
           | presumably at least in part because of the history.
        
           | nerdponx wrote:
           | Also, unlike Ruby, string literals are usually interned in
           | CPython (I think below a certain size), so they have at least
           | some of the performance benefits of symbols in Ruby.
        
       | kungfufrog wrote:
       | To be honest, while I've dabbled in Ruby, I've never understood
       | the difference on an intuitive gut level like other foreign
       | constructs unique to individual languages I've gone deep with.
       | 
       | I don't know what it was about this article but I feel slightly
       | more confused now. It jumps to bytecode before explaining how
       | they're useful at the higher level of abstraction that is the
       | developer.
       | 
       | How do symbols help you think about and find solutions to
       | problems and then implement those solutions? I.e., what is the
       | facility they provide that is not present in some more
       | traditional OOP language (say PHP?)
        
         | jrochkind1 wrote:
         | Yeah, it's an interesting article about symbol implementation,
         | but I think it's headline is wrong, it doesn't really discuss
         | why ruby has symbols.
        
         | ravi-delia wrote:
         | They're largely a holdover from smalltalk and lisps, which use
         | them as a sort of generalized token. I find them handy mostly
         | because you can express things like slot names, enums, or keys
         | in a way that's unambiguously not user provided. In Elixir for
         | instance (which has Ruby's symbol syntax slapped on Erlang's
         | atoms), you'll often report a failure with {:err, "error
         | message"} so you can pattern match on it. In principle, with
         | immutable strings, you could just have {"err", "error
         | message"}, and it would work the same! But that's hard to
         | distinguish from a list which happens to contain two strings.
         | 
         | Of course, the only thing prohibiting :err from being part of
         | the data is convention. But if you're just looking over the
         | code, the atom stands out almost like a syntactic feature, so
         | it's easier to hold to. Plus, since they're interned strings
         | underneath, you can use them in macros to make things like
         | schemas unambiguously, and convert them into migrations with
         | very little magic. So that's it, nothing you couldn't do with
         | interned strings, variable names, and enums, but all in one
         | handy little first class datatype.
        
         | matheusmoreira wrote:
         | String equality is linear time in the worst case. Symbol
         | equality is constant time. Symbols are strings that act like
         | numeric constants. They're extremely useful in APIs as
         | arguments, return values and generalized tokens. They're also
         | the obvious choice for hash table keys.
        
         | dgb23 wrote:
         | They seem similar enough to Clojure keywords:
         | 
         | Those are more focused and simpler than strings in say PHP. And
         | they are first class, unlike members in Java.
         | 
         | They are primarily first class names in your program. Think of
         | them as distinct elements in a set, as opposed to arbitrary
         | text to be transformed and parsed.
         | 
         | If I give you a symbol (or keyword) then you know it is a name.
         | If I give you a string, it could be anything really.
        
         | tomc1985 wrote:
         | The article is an extremely needlessly complicated explanation
         | of a relatively simple principle: symbols are stored once and
         | referenced by pointer, strings are stored multiple times and
         | not so easily compared. Ergo, for many use-cases symbols are
         | faster and use less memory.
         | 
         | Some of these use cases are little tokens like single words
         | that are used in as values in function arguments, or a switch
         | statement. On the other hand, storing the user's inputted name
         | as a symbol whilst copying that data to your model object is
         | probably not a good idea.
         | 
         | Yes this explanation leaves out a lot of detail.
        
         | bjoli wrote:
         | In scheme you usually use symbols instead of strings because
         | the whole equality story is simpler and much faster. I once
         | changed a tight loop in some code from dispatching on strings
         | to dispatching on symbols and got a 15% speedup. Why? String
         | equality is expensive. A symbol is basically a readable fixnum,
         | where two similar symbols are always the same objects (errr...
         | Don't quote me on that because it isn't strictly true).
         | 
         | Most places where you can use symbols instead of strings you
         | lose nothing and gain speed.
         | 
         | I am not sure how it is in ruby though.
        
           | berkes wrote:
           | In Ruby `"Foo" == "Foo"` returns true. Whereas
           | `"Foo".object_id == "Foo".object_id` returns false: They are
           | not the same object, but report being equal.
           | 
           | OTOH, symbols return true for both: they are the exact same
           | object.
           | 
           | It's not _just_ about speed though: symbols are somewhat
           | limited in what they can be made of. They follow the same
           | limitations as methods and variables. So often symbols are
           | used when dynamically calling methods or assigning variables.
           | 
           | "Foo".public_send(:strip!) 1. Which is slightly different
           | from "Foo".public_send('strip!'). Not in outcome, but in
           | calling. Because this is invalid syntax:
           | "Foo".public_send(:one-two three) whereas this isn't:
           | "Foo".public_send('one-two three'). Technically, I guess Ruby
           | can have a method that is named "one-two three" but that
           | would be really nasty to call. Symbols protect a lot against
           | this.
           | 
           | And therefore are used in this context a lot.
           | 
           | 1 The exclamation mark can be a part of a method and symbol
           | in ruby. As can the question-mark and some other sugar-ish
           | stuff like [].
        
             | matheusmoreira wrote:
             | > symbols are somewhat limited in what they can be made of
             | 
             | Only in the unquoted literal syntax. The :symbol form
             | follows Ruby's usual identifier rules but there's also a
             | :"quoted symbol" syntax. You can also send :to_sym or
             | :intern to any string and it will be converted to a symbol.
             | 
             | https://ruby-doc.org/core/String.html#method-i-to_sym
             | 
             | > This can also be used to create symbols that cannot be
             | represented using the :xxx notation.                 'cat
             | and dog'.to_sym   #=> :"cat and dog"
        
             | bjoli wrote:
             | Ruby inherited that ?! convention fromm scheme. All
             | mutating procedures end with ! and all predicates with !.
             | 
             | Prefix notation has none of those pesky limitations if you
             | can live with it :)
             | 
             | Edit: oh. Scheme is painfully monomorphic. Equality
             | for.strings is string=?. Equality for chars is char=?.
             | 
             | Then there is object equality (eq? ...), eqv? ("Normally
             | eq?") and equal? which is a generic equality predicate that
             | works for all objects (including circular data structures).
             | 
             | Eq? is the one you would use for symbols. Symbols are
             | always (almost, at least) eq?. One string is only eq? To
             | itself, but not a string containing the same content.
        
               | caseyohara wrote:
               | I think you meant to say this in your first sentence:
               | "all predicates with ?"
        
         | TazeTSchnitzel wrote:
         | The difference between symbols and strings only exists at the
         | low level. At the high level they are virtually the same thing.
        
           | eternityforest wrote:
           | It seems like the difference(As far as I can tell without
           | spending way more time with the article) is deduplication,
           | which other languages already give you with strings.
           | 
           | I think thr syntax is slightly nicer than quotes, but it's
           | also more syntax, and there is a limit to how much you can
           | have if you don't want code to look like Perl(Which Ruby is
           | approaching).
           | 
           | People are saying it can can be used for some kind of better
           | compile time checking, would be interesting to see that as
           | the main focus?
        
             | klibertp wrote:
             | > deduplication, which other languages already give you
             | with strings.
             | 
             | Aren't Ruby's strings mutable? I'm not sure, but I seem to
             | recall they were/are, in which case you can't really intern
             | them. Python and Java have immutable strings, with the
             | ability to optimize allocations being (probably?) one of
             | the reasons.
             | 
             | On the other hand, Symbols in Ruby seem to be immutable,
             | which allows for their interning.
        
               | TazeTSchnitzel wrote:
               | You can intern strings without preventing mutability
               | using the copy-on-write principle. PHP does it.
        
               | recursive wrote:
               | If you copy before you write, you're not mutating, you're
               | making a new thing.
               | 
               | e.g. This pseudo code must stand for mutation to be
               | present.                   a = ...         b = a
               | mutate(a)         /* b now mirrors a */
        
               | TazeTSchnitzel wrote:
               | A language runtime can make that work if it wants to.
               | High-level semantics don't need to constrain the
               | implementation.
        
               | noneeeed wrote:
               | It's optional. By default strings are mutable, but you
               | can freeze them individually, and you can set a directive
               | that makes all string literals as immutable on a file-by-
               | file basis.
        
           | jbverschoor wrote:
           | They are constants without having to declare them and assign
           | them a globally unique value. Strings are for text
           | (ui/parsing/values)
        
       | dorianmariefr wrote:
       | Symbols are garbage collected now so it's just shorter strings
       | 
       | IMHO it's a style choice
        
       | lamontcg wrote:
       | In the early days, symbols weren't garbage collected, while
       | strings were mutable, wasted memory and were slow. So there were
       | tradeoffs.
       | 
       | Now you can use frozen string literals and there's no benefit to
       | symbols. Throwing "# frozen_string_literal: true" in the top of
       | the memory benchmark script I get:                   Calculating
       | -------------------------------------                  strings
       | 0.000  memsize (     0.000  retained)
       | 0.000  objects (     0.000  retained)
       | 0.000  strings (     0.000  retained)                  symbols
       | 0.000  memsize (     0.000  retained)
       | 0.000  objects (     0.000  retained)
       | 0.000  strings (     0.000  retained)              Comparison:
       | strings:          0 allocated                  symbols:
       | 0 allocated - same
       | 
       | At this point with no practical difference between them STRINGS
       | AND SYMBOLS BEING DIFFERENT ARE A MISTAKE. When you serialize to
       | something like JSON you lose the distinction (the operation is
       | singular and does not have an inverse transform) and you have to
       | pick either symbols or strings to get back. On a long enough
       | timescale this causes enormous confusion, and leads to the
       | creation of hashes with indifferent access (which helps with the
       | problem, but doesn't fix it).
       | 
       | Ideally it would be good at this point to make symbols and frozen
       | strings completely equivalent ("foo".freeze == :foo being true)
       | but that would likely break too much existing code. The
       | differentiation between strings and symbols though only causes
       | code bugs (mostly biting the new and intermediate level
       | programmers). It is just syntactical sugar with a footgun.
       | 
       | Designing a language from scratch these days, it should have
       | immutable strings by default from the start and should not
       | introduce symbols, unless they are purely syntactic sugar around
       | creating an immutable string.
        
         | chrisseaton wrote:
         | > At this point with no practical difference between them
         | 
         | Gah no! This isn't true! Even if you turn on frozen string
         | literals comparing two strings is slower because they have to
         | test for a non-frozen and non-interned string also happening to
         | be the same.
         | 
         | https://twitter.com/ChrisGSeaton/status/1514603665801109508
         | 
         | There's a pointer comparison, but behind it is on the failure
         | side is a full-byte-comparison. Atrocious for cache even if the
         | strings are tiny. If they aren't you're checking every byte!
        
           | ravi-delia wrote:
           | I'm a big fan of explicit symbols, but some syntactic sugar
           | around immutable strings and type inference should let you
           | use "symbols" with little performance penalty. Of course by
           | then you might as well use explicit symbols anyway, but I
           | guess there's some additional flexibility. Of course that
           | hurts macro-writing a bit, but not much.
        
         | berkes wrote:
         | I both like and hate how Rust has `String` and `&str`. Constant
         | juggling between the two (which really is a sign I'm not doing
         | it right). Yet knowing and using the difference is important
         | and powerful.
         | 
         | I somewhat miss this when I go back to Ruby, but then realize
         | that symbols often can be used for `&str`. Often. Not always.
        
       | ktpsns wrote:
       | Languages with native symbol types are very helpful for... you
       | name it: Symbolic programming
       | https://en.wikipedia.org/wiki/Symbolic_programming
       | 
       | In the context of computer algebra systems, which are much about
       | manipulating abstract syntax trees, mathematical variables are
       | usually represented as "symbols".
       | 
       | Beyond that, this page gives databases as an example, which is in
       | fact very nice. Beyond being fast and efficient, using symbols
       | allows certain errors to be compile-time instead of runtime,
       | where typos are only detected on an application level and not on
       | a code level. This is where symbols can play out their advantage.
       | Think a bit of ENUMs in other languages.
        
         | smegsicle wrote:
         | the "you name it" idiom in english is usually used to mean
         | "anything you want", as in "you pick it"- so your first
         | sentence reads like "native symbols are useful for anything,
         | because (as everyone knows) symbolic programming is useful for
         | anything"
         | 
         | i think the idiom you were going for was perhaps "you guessed
         | it" or "you called it", as if poking fun at how, obviously,
         | native symbols are helpful for symbolic programming, because
         | it's the same word
        
       | avgcorrection wrote:
       | This seems to be a dynamic language thing. Could statically typed
       | languages have some uses for it? In Java one might use a lot of
       | constant strings which are keys for property files. You can of
       | course do some dynamic stuff in order to construct them but they
       | are more likely to be used as constant strings. Would using a
       | separate construct for that make optimization easier? Or wouldn't
       | it make a difference in practice?
        
         | dmitrytsepelev wrote:
         | Java does use string interning for constants. It can help in
         | any case when you have a lot of instances of the same string
         | (and when you need to compare that strings)
        
       | BurningFrog wrote:
       | When I started ruby, I thought symbols were weird and dumb.
       | 
       | After a while, it was one of the things I liked the most. Maybe
       | my favorite part is I didn't have to read and write so many
       | damned " characters!
       | 
       | "Converting" "text" "like" "this" :to :this :format, really helps
       | me read it.
       | 
       | I might be the weird guy on this. Wouldn't be the first time.
        
         | pdkl95 wrote:
         | While other comments have discussed the technical utility of
         | symbols, I believe symbols can also be seen as useful syntactic
         | sugar that helps communicate _intent_. Strings used for
         | indexes. named args, and other structural purposes can be
         | represented in a way that is visually distinct from strings
         | used as text.
         | 
         | The technical benefits are nice, but this type of ergonomic
         | feature is why ruby has remained my favorite language for over
         | a decade.
        
         | berkes wrote:
         | I was on the same page, but now moving away from that.
         | 
         | I more and more dislike how Ruby (arbitrarily) allows omitting
         | brackets. but not always. Often making the code harder to read.
         | What is the call-chain in this rspec magic:
         | `expect(something).to be >= 1` (quick: where and how do you add
         | a custom failure message).
         | 
         | And while `attr_accessor :time, :date, :state` are really neat,
         | I more and more dislike constructs like `validates :name,
         | :login, :email, presence: true`. And prefer to write them
         | explicit and unambiguous: `validates_presence_of(:name) etc`.
         | Which is only a very slightly improvement over
         | `validates_presence_of('name')`.
         | 
         | And don't get me started on "saving time" by typing less
         | characters or shorter lines of code: if this is what makes you
         | Go To Market faster, there's something very wrong with your
         | IDE, editor or typing skills. If anything, those short things
         | have _cost_ me time in Rails codebases living years and years.
        
           | Mikeb85 wrote:
           | > validates :name, :login, :email, presence: true`. And
           | prefer to write them explicit and unambiguous:
           | `validates_presence_of(:name) etc`. Which is only a very
           | slightly improvement over `validates_presence_of('name')`.
           | 
           | That's Rails, not Ruby. Although Ruby allows it because of
           | how flexible it is + metaprogramming.
        
           | wbkang wrote:
           | Yeah this threw me off so much that I wrote a blog about
           | this: https://wbk.one/%2Farticle%2Fa463c360%2Fthe-ruby-
           | tutorial-i-... It's weird how not clear tutorials are about
           | this.
        
           | BurningFrog wrote:
           | Half agreement on the last part. I never care about typing,
           | but I care a _lot_ about readability.
           | 
           | And fewer characters often is proportionally easier to read.
        
         | arcticfox wrote:
         | I'm in the same boat, FWIW. I _really_ did not understand the
         | point at first, but I love symbols now. I only vaguely
         | understand the point, even after reading this thread, but I
         | like using them.
        
       | charcircuit wrote:
       | How does this article not mention LISP? Ruby has symbols because
       | it was inspired by LISP which had symbols.
        
         | klibertp wrote:
         | No, Ruby has symbols because it was inspired by Smalltalk which
         | has symbols. Of course, Lisp also has symbols, and it was one
         | of the inspirations for Ruby (and Smalltalk), but the idea of
         | representing message sends (ie. method calls) as symbol +
         | arguments comes from Smalltalk.
        
         | dmurray wrote:
         | It's not a historical article tracing the lineage of the
         | feature.
         | 
         | Ruby took some things the designer liked from Lisp, from
         | Smalltalk, from Perl, from other places. He liked symbols
         | because they're good for performance (and compile-time
         | correctness) at a low cognitive cost, and that's why Ruby has
         | symbols.
        
       | Asmod4n wrote:
       | The difference between comparing symbols and strings in ruby.
       | 
       | https://twitter.com/chrisgseaton/status/1514603665801109508?...
        
       | luciusdomitius wrote:
       | I always thought Ruby has symbols because                  const
       | String ACCOUNT_FUNDS_EXCEEDED = "ACCOUNT_FUNDS_EXCEEDED"
       | 
       | is plain retarded :D
        
         | OJFord wrote:
         | That would be an odd way to do that in a language that _doesn
         | 't_ have symbols too though.
        
           | luciusdomitius wrote:
           | Have a look at 90% of corporate codebases.
        
           | zem wrote:
           | it makes sure that any typos are caught at compile time, for
           | one
        
       | mc4ndr3 wrote:
       | They're like enums but without the risk of colliding across
       | unrelated contexts. And because they're dynamically generated you
       | can have runaway symbol generation that triggers a memory
       | consumption problem.
       | 
       | Newbies try to use strings as enums, because they don't
       | understand enums. Symbols provide tradeoffs compared with both.
       | 
       | This is what you get with a dynamic language where people try to
       | overload functions with "I accept a scalar OR an array!!!"
        
         | cestith wrote:
         | From the caller's perspective there's little difference between
         | a procedure with variadic arguments and multiple dispatch.
         | There's a lot of difference from the implementor's perspective,
         | though. In Perl 5 you'd see a lot of ref() and wantarray()
         | while in Raku you'd see different signatures for procedures
         | with the same name.
        
       ___________________________________________________________________
       (page generated 2022-04-14 23:02 UTC)