6 ms·
as someone who has used ruby for years, and more recently got a job working in python, I think the most significant difference between the two languages is how
by zem 4y ago
as someone who has used ruby for years, and more recently got a job working in python, I think the most significant difference between the two languages is how they handle imports.
in python every file is also a module, and all imported symbols are namespaced within that module (unless you deliberately use "import *" to bring them into the current module's namespace). so you can define function foo in module bar and use it via "import bar; foo.bar()".
in ruby, modules/namespaces and files are two entirely orthogonal concepts; you define a module by enclosing code in a "module Foo; ...; end" block, and any file can open up the same module and add stuff to it. when you import a file it adds everything within it to the current global namespace, which makes it really hard to read a piece of ruby code and see where everything within it was defined.
for the most part where ruby and python differ I prefer the ruby way, but I feel like automatically namespaced imports is something python got right, and was a really big miss for ruby.
- typeofhuman 4y ago> so you can define function foo in module bar and use it via "import bar; foo.bar()". Wouldn't this be "import bar; bar.foo()"?
- 86J8oyZv 4y agoIn Ruby it’s “require” rather than “import.” But it would be more like “require bar; foo()”. However, Ruby does have auto-loading conventions. So you could define “module Baz{ module Bar { def foo end; } }” in “baz/bar.rb” from the root of your project. You should then be able to call “Baz::Bar::foo()” from any other file in your project structure without a “require” at all. This is not standard Ruby IIRC, but Rails and other frameworks use it.
- gls2ro 4y agoThere is a gem that does that used by Rails and multiple other gems: https://github.com/fxn/zeitwerk https://github.com/fxn/zeitwerk It is pretty easy to set it up in any Ruby project.
- khaledh 4y ago> You should then be able to call “Baz::Bar::foo()” from any other file in your project structure without a “require” at all. IMO, this is a downside rather than an upside. It's optimizing for writing rather than reading. This hurts understandability for someone who is not familiar with the code base.
- jonnytran 4y agoIMO, Ruby optimizes for reading. You simply cannot write concise code in Python as in Ruby. One reason (of many) is because of all the import statements required in Python. In Ruby, there is often only a single require statement, or maybe none at all, at the top of a file. The difference is convention over configuration. In Ruby, if you know the convention, then you know exactly where everything is defined without needing to read it. In Python, you must read all those import statements to know where something is coming from, not to mention write them in the first place. And if you really need to know where something is defined, you simply ask it. puts obj.method(:foo).source_location So IMO, Ruby optimizes for both reading and writing.
- dragonwriter 4y agoit would actually be: require 'bar' foo because require is a regular method and the argument is a string (I mean, you could pass bar without quotes if the string was in a variable called bar, but that's not the scenario being discussed.) > However, Ruby does have auto-loading conventions. So you could define “module Baz{ module Bar { def foo end; } }” in “baz/bar.rb” from the root of your project. You should then be able to call “Baz::Bar::foo()” from any other file in your project structure without a “require” at all. This is not standard Ruby IIRC autoload is a Kernel method in Ruby core, so it is standard Ruby, but you have to explicitly register a file to be automatically loaded when a particular method is referenced. Some systems additionally include code that does this for source files based on path.
- heartbreak 4y agoYes, it looks like OP had a typo.
- zem 4y agoright, that was my mistake
- lloeki 4y agoThe tradeoff is that you don't get to have open classes with a module oriented approach. Shameless plug: toyed with the idea of imports and "packages" in Ruby some time ago. Recent facilities in 3.1 (or 3.2?) could make the implementation even easier. https://github.com/lloeki/pak https://github.com/lloeki/pak https://github.com/lloeki/ruby-package https://github.com/lloeki/ruby-package IIRC there's an issue somewhere at bugs.ruby-lang.org about that EDIT: OMG 8 years ago! /me screams
- dragonwriter 4y ago> The tradeoff is that you don't get to have open classes with a module oriented approach. A module oriented approach could still have open classes, and if you did an imported module could still mess with classes in other imported modules (it would just also have to import them.) In fact, you can set variables in other modules and members (including methods) of other classes this way in Python.
- zem 4y agoi like that! it definitely makes things cleaner in terms of being explicit about where everything comes from.
- mountainriver 4y agoAgree Ruby code is super hard to understand, this seems largely overlooked by the community
- gls2ro 4y ago(out of curiosity) What is hard to understand about Ruby?
- mountainriver 4y agowas specifically commenting on the "magic" import system
- dolni 4y agoOne thing that drives me up a wall about Ruby (versus Python) is that function and method calls have basically zero context so you have _no idea_ where a piece of code actually lives without investigating or relying heavily on IDE features. Call a method on the current object? You just punch in the method name. There's no leading `self.` to indicate you're interacting with this class. If you require a library, the required code can be literally anything. Namespacing of the classes and functions is optional. Hopefully the library author did something reasonable. And even if they did, that namespacing isn't gonna match your require statement. Python's explicitness around this stuff is absolutely a breath of fresh air compared to Ruby's wild west approach. I also gotta say I'm not a fan of the fact that there tend to be one or two method aliases for lots of stuff in the standard library. It makes code easier to write but harder to read. As an example: the Array class has push, append, <<, unshift, prepend, and insert methods. Append is sensible. Do we _really_ also need it to be called push? and do we _really_ also need <<? Prepending is an uncommon enough operation that "array.insert(0, object)" should have been sufficient. I understand the intention behind Ruby's design decisions but I personally feel they do programmers a disservice. Writing (or even reviewing) Ruby without a featureful IDE is an absolute chore for projects of any appreciable size.
- gls2ro 4y agoFirst, I do agree that Ruby ecosystem could do some things better and IMO in the last year or so there are some good projects happening in this space. Second, I cannot refute your arguments. That is because I see them more of a personal preference or somehow expectations based on your programming history (I might be wrong of course as I don't know your programming history). As Ruby feels similar with other programming languages I think some kind of expectations of how things should be kick in. But Ruby has a specific design that is for example focused on "brevity" or "beauty" among multiple other thigs. I worked so far in the last 15 years in multiple codebases in Ruby: large Rails apps, Sinatra apps but also smaller webapps. In all kind of combinations some of them I built and maintained alone and some of them were built in a team. This being said, I don't remember a time when we have a big name conflict because we used a couple of gems and they were colliding. I do remember when some colleagues were over using meta-programming too much or changing default behaviour of stdlib. But this can be fixed with coding guidelines and I take imposing coding guidelines over removing these features from Ruby as sometimes they make more sense than anything else. Regarding ".self" I don't feel the need to use it. Once I got the call hierarchy I prefer not needing to write "self" where it is not needed. But also in Ruby everything* is an object thus I feel that self is a bit more powerful so it should be used when when it is really needed like when defining methods and you want to indicate where that method belongs. Regarding having multiple ways of doing the same stuff in stdlib, here we diverge as I prefer this diversity. I really like that we are multitudes in this community so code can be written in multiple ways, it is for me almost like a form of art when I read some case and I see how expressive can it be and how easy it is to get what it does even if it uses a different std method than I normally use. I think if you want to get Ruby you should start by thinking that Ruby is a programming language created (IMHO) with the intention to let you express yourself but also make it so that other people should easily understand it. It also worth reading this beautiful essay by the creator of Ruby: https://www.oreilly.com/library/view/beautiful-code/9780596510046/ch29.html https://www.oreilly.com/library/view/beautiful-code/97805965... (sorry I could not find a direct link unwalled) and from there you will get a glimpse of why Ruby is in a specific way and see if you agree or not with that. For example regarding your point with no leading self, Matz says in that essay "programs should ideally contain no unnecessary information" thus a lot of what you see in Ruby including having prepend instead of "insert(0, object)" is a manifestation of that.
- ufmace 4y agoIt's a possible smell in Ruby all right. Ideally, every file has its code in classes or modules that match the file name, so require 'foo' brings in the class Foo and nothing else. But it's possible to do some really ugly hacky stuff, so it more becomes please don't actually do that in a large production codebase. Python's default of namespacing imports is nicer all right, though I've seen a decent amount of Python code in the wild that has a dozen import * at the top of every file so that grep is the only way to find out where a function is actually defined, just as bad as Ruby.
- choward 4y agoRuby modules and requires effectively just concatenate a bunch of files that have globals in them and have nothing to do with real modules.
- jonnytran 4y agoThis is simply false. Ruby modules are first class. They're objects that you can pass around like any other object. You can put them in a local variable. You can define methods on them. You can do metaprogramming using them. You can build classes by mixing modules together. You can create modules (and classes) at runtime. Many things that need to be built-in in other languages are just a library in Ruby. Files and modules in Ruby are two distinct things. While Python makes files one-to-one with modules, Ruby allows you to define as many modules as you like in a file, zero or more. It's true that if you define a constant at the top-level of a file, it uses the global namespace. By convention, if you have a file named `foo.rb`, you'd write: module Foo # Code here end and it's equivalent to what's done in Python. But it also has all the other features.
- choward 4y agoYour claiming anything wrapped in a module block is a module. My point is that they aren't "real" modules. I know it's debatable what a module is but Ruby's don't meet my definition. Here's why: Let's say a file had a module in it. It can behave differently based on context. For example, let's say I have a module A that requires modules B and C. Module C could use module B without explicitly pulling it in because modules are just globals. However, if a different module just requires module C but not module B and no other files required B either, then module C would break when it tries to use model B. If modules weren't global B, would have to require it to use it directly regardless but since it doesn't it's easy to get unexpected results. In ruby a file doesn't have to declare the modules it uses which is why I don't consider them modules but just global namespaces. So if another module that got required earlier happens to require B, then B can be used because it's just a global. I'm summary, modules in Ruby aren't modules but just global namespaces.
- 4y ago