7 ms·
Why kill symbols?
by lancefisher 12y ago
Why kill symbols?
- bigtunacan 12y agoI was wondering this as well. Symbols are just atoms and atoms are great. The biggest issue with symbols in ruby is how gc is handled and that has improved in recent releases.
- TylerJay 12y agothirded. Can someone who doesn't like symbols help me understand the downsides of them? I really like how natural it makes passing around params that can be one of {x1,x2,x3...,xn} different values. Accomplishing the same with strings just feels messier and more prone to error.
- bigtunacan 12y agoThe only problem with Ruby's implementation of symbols was possibility of DoS which is resolved through garbage collection in the latest Ruby. Something that I found a bit ironic about the video linked in the article is at 5 mins 30 secs in; he states, "Why not give the memory address a name that makes sense to a person?" Here is referring to assembly and the abstraction it makes and how this relates to the abstraction that is variables. Symbols are really a cross system immutable memory address abstraction; I don't see how this is a bad thing.
- bigtunacan 12y agoSee djur's response to me in the thread. Apparently the cross processes piece is not true. So now that symbols are garbage collected there and the way "string".freeze works now the two are now basically the same construct.
- sferik 12y ago> Can someone who doesn't like symbols help me understand the downsides of them? I wish I had been clearer in my talk but I only had 30 minutes and wanted to cover other topics. Here is a more comprehensive argument against symbols in Ruby: In every instance where you use a literal symbol in your Ruby source code, you could you could replace it with the equivalent string (i.e. calling Symbol#to_s on it) without changing the semantics of your program. Symbols exist purely as a performance optimization. Specifically, the optimization is: instead of allocating new memory every time a literal string is used, lookup that symbol in a hash table, which can be done in constant time. There is also a memory savings from not having to re-allocate memory for existing symbols. As of Ruby 2.1.0, both of these benefits are redundant. You can get the same performance benefits by using frozen strings instead of symbols. "string".freeze.object_id == "string".freeze.object_id Since this is now true, symbols have become a vestigial type. Their main function is maintaining backward compatibility with existing code. Here is a short benchmark: def measure t0 = Time.now yield t1 = Time.now return t1 - t0 end N = 1_000_000 puts measure { N.times { "string" } } puts measure { N.times { "string".freeze } } puts measure { N.times { :symbol } } There are a few things to take away from this benchmark: 1. Symbols and frozen strings offer identical performance, as I claim above. 2. Allocating a million strings takes about twice as long as allocating one string, putting it in into a hash table, and looking it up a million times. 3. You can allocate a million strings on your 2015 computer in about a tenth of a second. If you’ve optimized your code to the point where string allocation is your bottleneck and you still need it to run faster, you probably shouldn’t be using Ruby. With respect to memory consumption, at the time when Matz began working on Ruby, most laptops had 8 megabytes of memory. Today, I am typing this on a laptop with 8 gigabytes. Servers have terabytes. I’m not arguing that we shouldn’t be worried about memory consumption. I’m just pointing out that it is literally 1,000 times less important that it was when Ruby was designed. Ruby was designed to be a high-level language, meaning that the programmer should be able to think about the program in human terms and not have to think about low-level computer concerns, like managing memory. This is why Ruby has a garbage collector. It trades off some memory efficiency and performance to make it easier for the programmer. New programmers don’t need to understand or perform memory management. They don’t need to know what memory is. They don’t even need to know that the garbage collector exists (let alone what it does or how it does it). This makes the language much easier to learn and allows programmers to be more productive, faster. Symbols require the programmer to understand and think about memory all the time. This adds conceptual overhead, making the language harder to learn, and forcing programmers to make the following decision over and over again: Should I use a symbol or a string? The answer to this question is almost certainly inconsequential but, in the aggregate, it has consumed hours upon hours of my (and your) valuable time. This has culminated in objects like Hashie, ActiveSupport’s HashWithIndifferentAccess, and extlib’s Mash, which exist to abstract away the difference between symbols and strings. If you search GitHub for "def stringify_keys" or "def symbolize_keys", you will find over 15,000 Ruby implementations (or copies) of these methods to convert back and forth between symbols and strings. Why? Because the vast majority of the time it doesn’t matter. Programmers just want to consistently use one or the other. Beyond questions of language design, symbols aren’t merely a harmless, vestigial appendage to Ruby. They have been a denial of service attack vector (e.g. CVE-2014-0082), since they weren’t garbage collected until Ruby 2.2. Now that they are garbage collected, their behavior is even closer to a frozen string. So, tell me: Why do we need symbols, again? I should mention, I’d be okay with :foo being syntactic sugar for a frozen string, as long as :foo == "foo" is true. This would go a long way toward making existing code backward compatible (of course, this would cause some other code to break, so—like everything—it’s a tradeoff).
- pmontra 12y agoImproved is an understatement. Symbols have been garbage collected only since 2.2.0 (December 25, 2014) and 2.2.1 has been released a few days ago with a patch for a corner case that was leaking symbols. Leaking them was what any Ruby < 2.2.0 was doing. jRuby added that https://github.com/jruby/jruby/issues/2350 https://github.com/jruby/jruby/issues/2350 but I think it's not released yet. Rubinius will follow but there is no estimates for a release https://github.com/rubinius/rubinius/issues/3280 https://github.com/rubinius/rubinius/issues/3280 Apparently Rails 5 will take advantage of symbols GC to use symbols as keys for the params hash instead of strings. It uses strings now only to prevent DoS because of uncollectable symbols.
- bigtunacan 12y agoYes; this is what I was referring to as well. The potential for DoS was a problem since they weren't garbage collected. I was thinking that symbol GC was introduced in the 2.1 series, but I see you are correct. I find it a bit surprising though that an article that is generally in favor of functional programming is bashing Ruby symbols. Ruby symbols come from Lisp and are just a form of atoms which are common place in functional languages. Erlang, Lisp, Elixir, Clojure, Scale to name a few all have atom/symbol support in some form. They are immutable even across systems. It just seems to run counter to the article's argument. Also there was no supporting reason why symbols should be killed. The article is released after GC was introduced for symbols so I'm assuming that's not the reason to "kill symbols".
- pmontra 12y agoActually it is part of the argument. I watched the video linked to the post. The speaker says that symbols and strings are converging. Frozen strings are symbol like and symbol GC made symbols string like. Symbols were a performance optimization and we are close not to need it anymore. I'm not sure there isn't more about symbols than that but I'll think about it the next time I'll write some Ruby. I'll pretend I have only strings and see what happens. One thing for sure: we'd need a syntactical shortcut because having to type "string".freeze everytime is unbearable. If the right shortcut is :string or the lispy 'string, I don't know but if we need it symbols are fine even if symbol.class could end up being String.
- kyllo 12y agoEverything in that list made me think "make Ruby less like Perl and more like Clojure" until I saw "kill symbols" and did a double-take. Symbols are awesome, they are perfect for map/hash keys. Symbols are from Lisp. Why kill symbols? They are even garbage collected now.