6 ms·
Ruby and The Principle of Unwelcome Surprise
- michaelcampbell 15y agoI'm really torn on the "no parens for func calls" thing. It's so damned handy for a majority of cases, but it breaks one my personal rules of "call things what they are". If it's a function call it should look like one. Then this unwelcome surprise wouldn't be a surprise at all. But, alas, I'm bound by Goedel too and am not 100% internally consistent. I saw Neal Ford's "Abstraction Distractions" recently and a lot of things resounded with me, but one that I liked was a general rule that abstractions should solve 80% of your cases easily, and get the hell out of the way for the other 20% and let you get further down the abstraction tree to solve them yourself. I believe this does.
- gtani 15y agoI seem to have met a lot of rubyists recently searching for better abstractions. Like OP many go to lisps, especially clojure (OP went to chicken scheme, which i 've never heard of before), and do away with whitespace/precedence/associativity parsing issues. Others look at static typing w/inference and/or better module systems (O'caml, haskell, scala, F# if you already own Vis. Studio). With these, whitespace/precedence/associativity parsing issues can be amplified or mitigated, depending on whose code you're reading, but once you absorb the language's Gestalt or Sprachgefühl they shd go towards mitigation. (Go and erlang are getting mindshare also)
- groovy2shoes 15y agoYou have a point that function calls should look like function calls, but keep in mind that, in Ruby, juxtaposition is how function calls look. The same goes for quite a few other languages as well (Haskell and ML off the top of my head, but there are others).
- michaelcampbell 15y agoYeah, I know it's not totally rational. Groovy does a trick where a Groovy'd Java class hides all the setters and getters and makes them look like property accessors. (C# does that too, I think?) And I like that, so I'm not necessarily arguing one way or the other; or at least I'm not arguing with the blog article. I have this argument with myself all the time! =)
- petercooper 15y agoWelcome to Bloggers and the Principle of FUD. Take this post with a pinch of salt. It's trendy to pick holes at Ruby in various, inaccurate ways lately, but a quote from Matz about that POLS business: Everyone has an individual background. Someone may come from Python, someone else may come from Perl, and they may be surprised by different aspects of the language. Then they come up to me and say, "I was surprised by this feature of the language, so therefore Ruby violates the principle of least surprise." Wait. Wait. The principle of least surprise is not for you only. The principle of least surprise means principle of least my surprise. And it means the principle of least surprise after you learn Ruby very well. For example, I was a C++ programmer before I started designing Ruby. I programmed in C++ exclusively for two or three years. And after two years of C++ programming, it still surprised me. You can pick up almost any programming language, not learn it properly, and then pick holes in its various inconsistencies or flaws (my own is over Python's ambiguities over methods vs functions for various things - but I know I'm not a Python developer!) That doesn't make it interesting or insightful. Every language has dark corners and Matz has been more than forthcoming about Ruby's. This article has numerous curious assumptions and sloppy practices. Local variables called the same as method names? And very few Rubyists, in my experience, would be confused by the key example in the memoization section. Who really thinks || checks for whether a variable is bound rather than doing a logical or? How is it a "what's worse" that instance_eval changes self to instance_eval's receiver? That's its entire reason for existing! The second example under "Local Variables Strike Yet Again" is flat out wrong, even by the author's own admission.. Ruby 1.9 has been the production release for almost three years!
- caiusdurling 15y agoA-bloody-men. I read the post and mostly went "What?". If you're calling local variables the same as methods in the same scope then you're just asked for trouble. Mostly because when you come back and read it 3 months later it's harder to pick through. Think the only related thing I've ever hit and gone WTF at is `foo = foo` sets foo to nil. But afaik that's due to the implementation of MRI/YARV, not all ruby runtimes. So yeah, as a professional rubyist, reading that post was mostly just me going "WTF, why would you ever think or do that". As Peter says, take it with a pinch of salt.
- autarch 15y agoI'm still amazed that neither Python nor Ruby allow you to require declaration of variables. Perl really got this right with strict and the "my" keyword. If you try to use an undeclared variable under strict mode, the compiler throws an error.
- gtani 15y agoruby warnings work pretty well http://davybrion.com/blog/2010/08/compilerinterpreter-warnings-are-important-learning-opportunities/ http://davybrion.com/blog/2010/08/compilerinterpreter-warnin...
- petercooper 15y agoIt's because Ruby scopes sigil-less variables locally by default. It's like using "my" everywhere for free. "x = 10" in Ruby creates a local variable. By default, "$x = 10" in Perl is creating a global package variable which, in most day to day cases, isn't particularly desirable. Perl's "my" really patches over a dangerous default. Perl gives you the global by default. Ruby gives you a local by default. I'd much rather have my defaults be sane than be forced to make declarations to fix things up. Which is why Ruby became my primary language after 8 years of Perl..
- davesims 15y ago...or in JavaScript getting 'var' everywhere for free.
- masklinn 15y ago> It's because Ruby scopes sigil-less variables locally by default. I'm pretty sure autarch knows that. I come from the Python world (and have done quite a bit of Ruby as well as a few other languages) and have come to dislike all implicit scoping, it is less readable and always ends up leading to annoying (and hard to track) bugs. This is magnified in Python due to scoping being historically wonky (there used to be no lexical scoping at all, only function-local and module-global) and leading to `global` and (in Python 3) `nonlocal` (yay two crappy keywords and statements instead of having a single clear one) Whatever the case, these will trip people up and really provide little to no gain, explicit lexical scoping is clearer and leads to less bugs, both from shadowing an existing variable and from hooking into (and overwriting) the same. It's also pretty easy to statically check, in a statically (lexically) scoped language. Interestingly, that (along with first-class blocks, keyword messages and the cascading operator) is one of the very nice things Ruby threw away from its (limited) Smalltalk ancestry. > Ruby gives you the safe, local and often wrong > thing by default.
- albertzeyer 15y agoDoes that mean that `x = x + 1` does not work in Ruby? (And always results in `x == 1` or so?)
- cheald 15y agoIf x is initialized to an integer (or more accurately, any object that has a method "+" defined that knows how to accept a Fixnum parameter), it'll work fine. If it's not, you'll get "No method + for nil", since what's happening is that you're trying to invoke a method named "+" on the variable named "x" with the parameter "1".
- deleted 15y ago[deleted]
- cheald 15y agoThe first issue isn't really surprising once you understand how Ruby treats variables. I wrote about this a while ago for a more understandable case - basically, Ruby initializes variables to nil as soon as a left-hand assignment is encountered during a scope's parsing, regardless of context or reachability. http://www.coffeepowered.net/2009/03/02/syntactic-sugar-will-occassionally-kick-your-puppies/ http://www.coffeepowered.net/2009/03/02/syntactic-sugar-will... Here's a cleaner way to visualize it: def foo if false bar = "baz" end lambda { bin = "bang" }.call return local_variables, bar end def foo_two if false bar = "baz" end lambda { bar = "bonk" }.call return local_variables, bar end puts foo.inspect puts foo_two.inspect => [["bar"], nil] [["bar"], "bonk"] Obviously the code that initializes bar never gets run, but Ruby initializes a local variable "bar" anyway, since there is a left-hand assignment to it present in the method. The lamba defines a new scope, so its local variable assignments don't leak to its containing scope, but in that scope, unless there was a variable named bin initialized in the outer scope already, which is the case for the lambda in foo_two, which leaks the bar reference to its containing scope since bar was defined in the outer scope at the time of the lamba's definition.
- tantalor 15y agoThis is mainly caused by the false intuition that @foo ||= bar actually expands to @foo = @foo || bar which, to many a surprise, is not the case. Hardly a "false intuition", since that is how this syntax works in other languages, e.g., perl[1]. [1] http://docstore.mik.ua/orelly/perl/prog3/ch03_17.htm http://docstore.mik.ua/orelly/perl/prog3/ch03_17.htm
- jonnytran 15y agoA lot of Ruby is kind of like the traffic circle. Circles can be a superior intersection of 5 roads (if traffic volume isn't high, you never have to stop), but they trip up nearly all new drivers. I've found that Ruby is a very right-brain language in the sense that when you analyze it at a low level, things don't seem to make sense. But at a high level, it often does exactly what you'd want. Variable hoisting in 1.9 is the perfect example of this. OP claims to have been a "Rubyist", but it sounds like he's still fighting the system and hasn't embraced it yet. It's like when people who are new to Lisp complain about the parentheses or prefix notation. If you just go with it, down the road you may find that these are some of its best features.