15 ms·
> Unlike Python, in Ruby… There are a number of shortcuts that, although give you more to remember, you quickly learn. They tend to make Ruby fun and very produ
by boberoni 4y ago
> Unlike Python, in Ruby… There are a number of shortcuts that, although give you more to remember, you quickly learn. They tend to make Ruby fun and very productive.
I use Python, but have never tried Ruby. The ecosystem for Python packaging is so chaotic that I’ve considered switching.
Any Pythonistas/Rubyists here that could point out some Ruby shortcuts that make you more productive?
- z9znz 4y agoHere's a rough list of benefits of Ruby compared to Python. It's not well organized and the examples are difficult to show in a comment box, but I have pages of notes of why Python is frankly a horrible language compared to Ruby. Every expression returns a value, so you can assign the result of an if/else or switch statement to a variable. Within your conditional clauses, you don't have to repeat assignments or return values. If the logic goes down that branch, then the last value in that block is the return value. This also means you don't have to have explicit 'return' statements in functions. Whatever the last value of the last expression is, that's your function return value. You can use an explicit return if you want to exit the function early, however. Ruby has a switch (case/when) statement. Python finally has this in 3.11 I believe, but it took forever to get. Hash.dig(). If you have nested hashes (dicts), you can myhash.dig(:top_key, :lower_key, :even_lower_key) In objects, you can control which attributes can be read/written via attr_reader, attr_writer, attr_accessor. So you can make an attribute have a default getter if you use attr_reader :some_attribute, and then you can write your own setter if you want to control how it gets assigned to. You can even simply not have a setter, so you can effectively make an attribute read-only. Clean, simple hash initializers: h = Hash.new { |h, k| h[k] = { age: 0, nickname: k } }. So now you can just start assigning to values in h like h["bob"][:attr1] = 28, and now h["bob"] refers to a hash with age 28 and nickname "bob". Yes Python has defaultdict, but it's an extra thing you have to use, and it's clumsy compared to the Ruby approach. Hash (dict) merging: It's a mess in Python. In ruby it's just h1.merge(h2), or even h1.merge(key_to_update: 123). And you get a new hash as the result. Or you can merge in place with merge!(), changing the original. Function names with ! and ? greatly simplify function naming (and improve readability). So instead of is_foo(), you just have foo?(). And if you want to indicate that you are doing something destructive, you name it foo!(). This is a common pattern in Ruby libraries, and you often have functions which return a new value and functions with the same name followed by ! which modify the input value. String.strip() returns a new string without leading/trailing whitespace, while strip!() modifies the input string to the same effect. Python's list comprehensions are arguably ugly and messy and harder to visually parse when reading. Ruby has consistent ways of doing operations on collections. This would take a full page to show examples. Copying and pasting code in Python often results in new code which is indented wrong (in the wrong scope), and which you must manually fix. This is not a problem in Ruby, since there are actual 'end' statements. Editors know how to properly indent what you paste. Safe navigation. myobj&.someattr&.foo will give you the value of foo or nil if myobj or someattr don't exist. You have to use ifs to do this in Python. Array flattening. nested_arrays.flatten(). But in python, you have to use something like list(itertools.chain(*arr)), but that fails if the first element of the list is an int (not an iterable). Gettings hash/dict keys. In Ruby, you just do myhash.keys and you're done. But in Python there are several ways to get the keys, none of which is as simple and obvious as just keys(). Not allowing typos to silently fail. With Python, you can take an object and assign a value to a non-existent attribute. You might do this accidentally if you have a typo when trying to set an actual attribute: myobj.first_Name = "bob". Python will happily do that, and you'll later debug and wonder why myobj.first_name == "" (and then notice that you have this similarly named attribute now called first_Name). Duh. I guess you could say it's convenient to be able to do this, but it's more a footgun. Ruby will bark at you if you try this. List sorting in Python is in-place, so you can't non-destructively sort and get the sorted list returned. In Ruby you have a choice. sort() is non destructive, and sort!() is in-place. I've got more, but this is enough.
- maligree 4y agoUsing the sorted() built-in returns a new sorted array. Getting keys from a dictionary is literally just calling dict.keys(). Sorry, but I don't think you've used Python enough to offer lists like these.
- z9znz 4y agoI stand corrected on the mylist.sort() vs sorted(mylist). But really, such a mistake is more likely with two very different ways of doing the same kind of operation. In ruby you would just mylist.sort() for a new sorted list (although parens are optional if no arguments, but I show them), or mylist.sort!() to sort mylist in place. You don't have to know two very different things. Plus it's obvious with the ! > Getting keys from a dictionary is literally just calling dict.keys() Nope. Unless your definition of "keys" is a 'dict_keys' class. Chances are really good that what you wanted was a list of keys, not some object which does not behave exactly like a list. If you want to access the keys by index, you can't do it like mydict.keys()[0] because dict_keys does not have an index method. Oddly, if you want a list of keys of a dict in Python, you just do list(mydict). But you can also do [*mydict], which gives you a list of keys but is something special you have to learn. Here's a fun one. Give me the first key of a dict. Ruby: mydict.keys.first Python: (several ways) next(iter(mydict)) # which makes no sense at all... asking for the next value of something when you're actually asking for the first. list(mydict)[0] [*mydict][0] What if you want the values of the dict? Well, no surprise, Python gives you a dict_values object when you do mydict.values(). Likewise, you can't do listy things with that because it's not a list. You have to convert it to a list with list(mydict.values()). Or you can do the [*mydict.values()] thing. Python is full of so many inconsistencies, the cognitive overhead is much higher than it needs to be. I would argue that it makes people dumber, because they have to waste mental energy learning weird things for special cases. But what about the rest of my previous post list? Here's one I left out. The return-early pattern is common in imperative programming. We like to exit a function as soon as we know things aren't going a certain way. In Ruby: x = fetch_object("bob") or return "bob not found" In Python you need 2 or 3 lines (2 if you're using the walrus := operator). if (x := fetch_object("bob") is None): return "bob not found" How about setting a value for a variable if it is unset? This is a common thing done when you have variable arguments Ruby: x ||= "default" Python: x = x or "default" # not only does it look stupid setting a thing to itself, but it will fail if x is zero. Edit: fixed asterisks.
- batiste 4y agoIt really doesn't... After years using Ruby my mind never managed to parse its complicated grammar.
- travbrack 4y agoAny examples? I always found it easy to grok but I'm not at a very high level so I'm curious where it gets hairy.
- cycomachead 4y agoThere's a bunch of things to pay attention to in Ruby. I wouldn't say these get too hairy, but they trip students up for very understandable reasons. * `a ||= b` is not just `a = a || b`, but `a || a = b` * The distinction between defaults arguments and hash arguments in functional signatures has many edge cases -- but it's much much better in Ruby 3. * There are still 2 pieces of syntax for hashes (`{a: 1}` and `{:a => 1}`) They are the same. * Curly bases show up a fair bit for a language that's not a braces language. :) They're in hashes, blocks, and % strings. e.g. `%w{a b c}` is the same as `%w|a b c|`. They're also used in string interpolation, e.g. `"Hello, #{name}"`. * There are sometimes many ways to do the same thing. There's Procs, blocks, and lambdas. % strings work with many characters. * `unless` for `if not...` and get wonky. * Overusing "surprise if" e.g. `do_thing if other_thing` can be hard to read. * It's so easy to monkey-patch and extend primitive classes, you might not realize when libraries do it. All of these are features that I actually find nice to use in the right contexts. But they can pop up when you don't expect, and they can definitely make Ruby a challenge to learn. That said with a little discipline (or just a linter ;)), I find a lot of Ruby code a joy to read and write.
- IshKebab 4y ago> `do_thing if other_thing` Yeah I don't get why you would deliberately make the flow control confusing and backwards like that. Python list comprehensions are the same. They make the information flow backwards. This also has the effect of making nesting really confusing.
- Hamuko 4y agoYou should probably try some 10-minute Ruby tutorial to see if the syntax doesn't completely scare you off.
- Gordonjcp 4y agoWhen I looked at Ruby (admittedly a long time ago now) everything was 10 minute videos. Is there any actual useful material on it now?
- regularfry 4y agoThe default guide for a long time was the Pickaxe book, which solidly pre-dates the availability of 10-minute videos by several years. There are a fair few links here that you might find useful: https://www.ruby-lang.org/en/documentation/ https://www.ruby-lang.org/en/documentation/
- nomilk 4y agoThere's ~50 short exercises/lessons here: https://learnrubythehardway.org/book/ https://learnrubythehardway.org/book/ I did them when I started out, and occasionally revisit as a refresher. It doesn't teach everything in ruby, but it absolutely gives enough to do just about anything you could want.
- cycomachead 4y agoMore productive is really hard to say... But I personally prefer Ruby to Python. For smallish scripts, I really like Ruby's backticks for easily executing shell commands. And I find the metaprogramming support in Ruby great for some tasks. I'm not sure if that's what you're getting at. But especially with "modern" Python features like f-strings and the walrus operator there's not tons of differences that really stand out as "productivity". One thing to just remember: Ruby is not just the Ruby on Rails community. Rails is a dominate space, but it's not the only thing out there. At the same time, when it comes to scientific computing, ML, etc. the Python ecosystem is incredibly robust. There's plenty of awesome Ruby tools for these things too, but it is not the same. Ruby and Python are the two languages I primarily teach courses in, and used professionally. Ruby is my cup of tea personally, but they're both modern dynamic interpreted languages.
- ptx 4y ago> For smallish scripts, I really like Ruby's backticks for easily executing shell commands. Executing external commands is pretty convenient in Python these days, now that we have subprocess.run [1], and passing the command as a list rather than through the shell seems a lot safer. Doesn't backticks open up the classic command injection issues (i.e. the same kind problem as SQL injection with non-parameterized queries) unless you're extremely careful to manually apply proper quoting in every single case? [1] https://docs.python.org/3/library/subprocess.html#subprocess.run https://docs.python.org/3/library/subprocess.html#subprocess...
- noirbot 4y agoIt would, but most of the time when I'm using backticks, it's because I'm writing myself/my team a little helper script that will never get "injected" with anything. Backticks (and the other more featured ways of doing shell commands in ruby) make it really easy to write what amounts to a Bash script, but with a little more features and readability, or pull in some relevant libraries for some more complex task that would be annoying to do in pure Bash.
- ptx 4y ago
- oezi 4y agoI think with "shortcuts" they mean things such as $1 (first match group of a regex) or =~ (regex match operator). I general I rarely feel like I am unproductive in Ruby (or bogged down in boilerplate typing).
- cycomachead 4y agoIn that case -- I definitely agree Ruby usually gets out of way. It's often my go to for hacking on something, unless it's data science-y. I'll mention: implicit returns, return if, and safe navigation, the &. operator. Ruby's newer pattern matching features are really cool[1], especially for things like JSON structures, but I've only use started using them. Classes are also super easy to extend. Many of these things are also incredibly easy to abuse - but they have their places. [1]: https://docs.ruby-lang.org/en/3.0/syntax/pattern_matching_rdoc.html https://docs.ruby-lang.org/en/3.0/syntax/pattern_matching_rd...
- transfire 4y agoI’m not sure what “shortcuts” the author is referring, but perhaps… x = y || z # if y is falsy x = z x = f ? y : z # ternary operator %w{one two three} # array of words And so on.
- endorphine 4y agoAlso: x ||= 1 x &&= 1 and the various built-in methods from Array, Hash, Enumerable, String etc.
- intuxikated 4y agoThese all exist in python, though you use or/and instead x = y or z x = f and y or z you can also use inline if/else x = y if f else z 3rd one just seems like an array of strings? ["One", "two", "three"] Not needing quotes for single words saves some typing tho.
- deleted 4y ago[deleted]
- pmontra 4y agoI loathe most shortcuts: too many things to remember and usually cryptic syntax. Maybe that's because I have to be a polyglot developer (Elixir, JavaScript, Ruby, Python) and I can't remember all the tiny details of each language. If you want to give a try to Ruby, get to understand blocks quickly. Instead of for item in list: work_on(item) which would work (with a closing end and no : ), the idiomatic way is list.each do |item| work_on(item) end or the one liner list.each {|item| work_on(item)} Check also map, select, each_with_index, each_with_object, etc. Blocks are fundamental to Ruby for reason that only practice can tell.
- greenpeas 4y agoOr with numbered parameters (since ruby 3.0) list.each { work_on(_1) }
- pmontra 4y agoWhich is exactly the kind of shortcut I loathe.
- greenpeas 4y agoyou're not alone :) https://bugs.ruby-lang.org/issues/15723 https://bugs.ruby-lang.org/issues/15723 This syntax looks ugly, but it's really nice when writing short scripts. Combined with one line function declarations: `def foo(n)= bar`, it can make an irb session much faster/shorter.
- BiteCode_dev 4y agoSwift has this as well, and it's really nice. Doing map(lambda x: x + 1, stuff) or [x + 1 for x in stuff] adds nothing of value for those very simple cases. I wish we had map($1 + 1, stuff), or ever better, stuff |> $1 + 1. Python does a lot of things right, but for a language with a strong emphasis on the iterator design pattern, I wish it would provide more expressive primitives. However, it comes with the FP territory, which Python is not well suited for, and it would also imply a higher cost of entry to beginners.
- 4y ago
- shaicoleman 4y agoThere are thousands of little things that make Ruby nice. They all add up to a more enjoyable experience. I'll give you one tiny example: Python: from datetime import date if date.today().weekday() == 5: print("It's Saturday") Ruby: require 'date' puts "It's Saturday" if Date.today.saturday?
- ouija 4y agoI prefer the Python code here. The import is explicit, so if you want to know where "date" comes from, that's easy. In a modern IDE I don't write imports manually anyway. You could write it as a one-liner in Python as well, but I would prefer not to. print("It's Saturday") if date.today().weekday() == 5 else None That the Python library doesn't define constants for days is unfortunate, but not a limitation of the language.
- bsaul 4y agoFrom what i've seen ruby is definitely on the downward slope regarding adoption and python has become a defacto standard in some fields. Its ecosystem is much better in pretty much every domain, so i don't think there's any point switching at the moment, honestly.
- jillesvangurp 4y agoI've used both. I slightly prefer python over ruby. But I haven't used either recently. Ruby's strength is internal DSLs. Python's strength is that it's simple to pick up for people. There's a large community of people that are not primarily programmers that use it. The reason for this is that it is a very easy language to get started with. Both share the same problem: they are weakly typed scripting languages. Both have in common that type annotations have been retrofitted to the language. But it's an uncomfortable and awkward retrofit in both cases and their respective communities seem to regard using types as an optional and inconvenient thing. Ruby with types of course exists. E.g. Crystal is nice apparently. But I've not really seen that used in the wild. Python is enormously popular; ruby's popularity seems to have declined a lot. I know a lot of former Ruby programmers that have moved to different languages and only a handful that still use it. It's not an obvious language to pick up for younger programmers. There are so many other languages to choose instead. IMHO the key innovation in language design in recent years has been type inference. This has lead to a new breed of languages that keep most of what made python and ruby popular: the ability to write code with a minimum of verbosity but without sacrificing type safety. Whether you use Kotlin, Typescript, C#, or even Java: they have type inference now and it reduces the verbosity that comes with having types. Scripting vs. compiled languages is much murkier these days where scripting languages have compilers or transpilers and linters and compiled languages have REPLs and all languages can now run in a browser via WASM anyway. The distinction is just not that relevant anymore. I write a lot of Kotlin, have dabbled a bit with typescript and I don't think I'll ever use languages without proper types again. Just not worth it for me. I don't see the value of not having a good typing system. Kotlin is interesting to me because internal DSLs are a thing with it; just like they were a thing with Ruby. And it can use javascript as a compilation target. Ruby has a slightly nicer syntax for DSLs than Kotlin for internal DSLs. But I don't mind curly braces and having strong typing enables a lot of good stuff. Like proper IDEs with refactoring, autocompletion, auto-fixes, etc. which are all very nice things to have in a DSL. I use Kotlin DSLs for writing CSS, HTML components (we use kotlin-js), writing elasticsearch queries (I maintain the multi-platform kt-search library), gradle build files (which uses kotlin script), and quite a few more things. IMHO, it could do a lot of things that python is popular for as well.
- davidthewatson 4y agoI've been using python for two decades and ruby exactly one decade ago for a couple years. Your comment on packaging is interesting. My perspective is that packaging on ruby was in nearly the same state ten years ago as python is now. My team switched ruby package managers several times during the ruby work and none of them ever worked reliably and was a regular root cause of fragility in our builds. I hope that ruby packaging has settled in that time while python has the same kind of cultural noise that afflicted ruby during that period. Popularity has downsides. Political packaging wars is one of them. I'm old school enough that I stick with tried and true pip, venv, and .env and its ilk. There was a long battle in downstream packaging for a while that I just didn't want to be involved in having seen the same thing in ruby. FWIW, if I were you I'd look at crystal, elixir, or nim. I was very productive in ruby but the near constant battle of wrestling with DSL's that are not concomitant with their parent is, IMHO, to be avoided. I've seen more than one team fall prey to that trap. Caveat emptor - pick your DSL's carefully.
- kawsper 4y agoWhat do you mean by switching packaging managers? Ruby uses Rubygems and Bundler to manage packages. Are you referring to Ruby version managers that manages different versions of Ruby (rbenv, rvm, asdf, etc.)?
- ByteJockey 4y agoThey mention doing this a decade ago. They were probably using utilities to manage downloads from the ruby application archive (which would have still been around at the time) and automate running `setup.rb`
- davidthewatson 4y agoYes, all of the above. Sorry for the unclear language. At the time, I recall it being very distracting when the team was focused on shipping well-tested code. We had a world class devops guy, but we all ran into these build issues frequently enough that it was a regular complaint at retrospectives no matter what tooling we were using at the time or who was using it. It was not long after this that I read Daniel Greenfeld's book on Django that introduced the notion of "vanilla django" and that resonated with me, precisely because our rails tooling was decidedly not vanilla. My take now is that rails can have the same problems with regard to being "off the rails" WRT the aforementioned DSL's and such. I don't think the root cause is ruby as much as there are pockets of rails that are far from what DHH describes.
- nickjj 4y ago> Any Pythonistas/Rubyists here that could point out some Ruby shortcuts that make you more productive? It depends on what you want to do. If it's building web apps there's Ruby on Rails, which is written in Ruby. In my opinion there's no alternative to this in Python. Sure there's Django but it's not the same even though both are considered batteries included web frameworks. One nice thing is you'll find both Python and Ruby SDKs for a ton of third party services (like Stripe, etc.). That goes a long ways for building things quickly and with confidence. Ruby's package manager is also a lot better IMO. It supports proper lock files out of the box and everyone uses gems with bundler. In Python it's a mix of pip, pipenv, poetry, etc. all of which have their own unique issues. With Ruby I never felt like I had to battle this aspect of the language. You just bundle install what you want and creating gems is the same process.
- lloeki 4y agoIf one wants to get into Ruby, please stay away from Rails as a first step into Ruby-land. Look into Rack and Sinatra first, then Rails. Even if it's a short trip it's going to be worth it.
- Lio 4y agoThis is personal opinion but these days I'd probably swap Sinatra for Roda[1] for small API services. It's generally faster, uses less memory and is a really good example of well written ruby code, IMHO. I also really like Jeremy Evans' book, Polished Ruby. 1. http://roda.jeremyevans.net/index.html http://roda.jeremyevans.net/index.html 2. https://www.packtpub.com/product/polished-ruby-programming/9781801072724 https://www.packtpub.com/product/polished-ruby-programming/9...
- MatthiasPortzel 4y ago> Sure there's Django but it's not the same Could you elaborate on this? I’ve used both Django and Rails and in my mind they’re direct parallels to each other. For example: in Rails you write migrations and then it generates a schema.rb, and in Django you update your model schema and then it generates migrations. They have very different philosophies, sure (reading the Rails manifesto made that clear), but from a technical standpoint they have pretty good feature parity.
- AnthonBerg 4y agoI like the RubyMine IDE a lot. The code intelligence is very good – it’s so good that it tends to teach you Ruby as you use it. Code intelligence as in code navigation, easy access to documentation on the fly, refactoring, code style suggestions. The same goes for the tooling integration. It works with most of the development tools you’d use with Ruby and captures the integration pretty well in a UI and associated workflow. Link for your convenience: https://www.jetbrains.com/ruby/ https://www.jetbrains.com/ruby/ (No affilition except being a paying user.)
- gls2ro 4y agoI am using both Vim and RubyMine and I strongly recommend RubyMine for anyone starting with Ruby. Having the possibility to have a Go to definition for almost any method is great and most of the gems have great code comments with a lot of nice examples. When I teach people to learn Ruby (I do this sometimes) among the first things I do is I explain to them how to open a gem, look at the source code, read it and then try to use it. I cannot thank enough the maintainers of the most popular gems for doing such a great job in having great ruby code and good in-file documentation.