1. 91
  1. 41

    You can tell this very easily if you’ve written any “pure” Ruby in the past and you’ve had to search for how to do something on SO. There’s so many (mostly old) answers that use methods that are only available in ActiveSupport and most of the time they don’t even mention it because they assume you’re using Rails.

    I used to write a lot of Ruby on my own but I never used Rails (though I did end up reimplementing ActiveRecord once or twice…) and I remember this issue driving me crazy sometimes.

    It’s a lot like JS and JQuery a few years ago, if you wanted to write something in pure JS and ended up on SO while searching for how to do something, you had to trudge through a lot of JQuery before finding a pure JS solution, if there even was one.

    1. 10

      Yeah, back when I was doing a lot of Ruby it would drive me nuts to find a SO answer with ActiveSupport/Rails-specific code for general Ruby questions. Which is a shame, because after I learned to tell the difference and dug deeper into learning “pure” Ruby, I had a much deeper appreciation for the language and tended to write much cleaner code. There’s a certain point where training wheels take over a dialect/ecosystem and become more of a hindrance than an asset.

    2. 33

      I find it so interesting thinking about the way that programming languages can reflect natural language with dialects.

      I feel like you could also think about writing with a particular ‘accent’ when you are writing code that isn’t idiomatic to the language you are using. For example, if you came from Go to Ruby you might start writing a bunch of for loops, which is valid but not typically how Ruby is written. You are speaking Ruby, with a Go accent.

      1. 14

        The world has seen so much bad Fortran code that the name of the language is now a synonym for bad coding. Many of us have never seen real Fortran code, but we know what coders mean when they say, “You can write Fortran in any language.”

        How Not to Write Fortran in Any Language

        1. 8

          This has been my exact experience with Ruby. I started using it within Amazon, where they utilize live pipeline templates (LPTs) that are packages built out with layers and layers of monkey-patched Ruby, and these in turn spit out generated build artifacts, e.g. Cloudformation templates.

          Now my current role makes use of a Rails monolith and even after some time, the differences are still jarring and I’m still trying to rid myself of the muscle memory from my AWS experience, speaking that LPT dialect of Ruby, as you put it.

          1. 4

            Having maintained LPTs, Octane templates, etc., you’re exactly correct. Horrifying kludge, but we’re steadily replacing it with.. TypeScript, Java/Kotlin and Python. ;)

          2. 6

            programming languages can reflect natural language

            I think a lot about how Perl was designed by a linguist, and it shows.

            1. 4

              In a good way, or in a bad way?

              1. 8

                Yes.

                (In all seriousness, I suspect a bad way for actually implementing it; does an independent implementation of Perl 5 that’s compatible mostly exist? But it is fascinating less from a PLT and more a “you can phrase it like that?” angle you see in NLP…)

            2. 4

              A lot of programming languages have constructs that “infect” the code base. Once they’re in use, you have to keep using them. Some I can think of:

              • async in Rust
              • Rc vs owned in Rust
              • null vs Optional in Java
              • FP vs OOP style in Scala
              • Akka in Scala
              • naming schemes (tends to be a problem in older languages like Python or C++)
              1. 5

                I’m confused about the mention of Rc here? Rcs are owned. What I think is a infecting problem is that you can’t be generic over Arc vs Rc.

            3. 15

              I completely agree with the points the author makes, but not the summary. To say that a monkey-patched DSL of this sort “isn’t Ruby” is like arguing that any particular use of macros “isn’t Lisp”.

              That’s correct in the sense that the language has been extended (far more elegantly in the latter case, I’d argue), but extension in that manner is idiomatic use of the language itself. To take a concrete example from Common Lisp:

              CL-USER> (loop
              	   for x in '(1 2 3 4)
              	   sum x into y
              	   collect y)
              
              (1 3 6 10)
              

              That LOOP is most certainly not Lisp … for, sum, collect, read more like an RDBMS dialect. However, it also most certainly is Lisp because …

              CL-USER> (macroexpand `(loop
              			 for x in '(1 2 3 4)
              			 sum x into y
              			 collect y))
              (BLOCK NIL
                (LET ((X NIL)
              	(#:LOOP-LIST-648
              	 (SB-KERNEL:THE* (LIST :USE-ANNOTATIONS T :SOURCE-FORM '(1 2 3 4))
              			 '(1 2 3 4))))
                  (DECLARE (IGNORABLE #:LOOP-LIST-648)
              	     (IGNORABLE X))
                  (SB-LOOP::WITH-SUM-COUNT #S(SB-LOOP::LOOP-COLLECTOR
              				:NAME Y
              				:CLASS SB-LOOP::SUM
              				:HISTORY (SB-LOOP::SUM)
              				:TEMPVARS (Y)
              				:SPECIFIED-TYPE NIL
              ;; ... snip ...
              	  (GO SB-LOOP::NEXT-LOOP)
              	 SB-LOOP::END-LOOP
              	  (RETURN-FROM NIL
              	    (SB-LOOP::LOOP-COLLECT-ANSWER #:LOOP-LIST-HEAD-649)))))))
              

              So while it’s fair to say that LOOP isn’t Lisp … also, in a more fundamental sense, it is, because Lisp is for creating DSLs (in the same way that Ruby is).

              1. 5

                because Lisp is for creating DSLs (in the same way that Ruby is).

                This of course depends on your perspective, but as a long-time ruby programmer I strongly agree with author and strenuously avoid monkey-patching and DSLs in ruby. I’m not really interested in whether that is the “true” ruby or the wild-west monkey-patch dsl-fest version is. I just want to write simple and maintainable code, and have found that avoiding those features entirely serves that goal.

                1. 9

                  The downsides of monkeypatching are not related to DSLs. DSLs are good, the author has written many of them.

                  DSLs are bounded. You don’t need to pollute other people’s code to build one. Totally different topic of conversation.

                  1. 7

                    Yes, it is a different topic of conversation. And I have used dry-ruby extensively.

                    But separately I think DSLs are bad. Not 100% of the time, but mostly. Also, I’ve noticed a correlation in that if people are drawn to one, they are often drawn to the other.

                    In the ruby community, DSLs are way, way overused. I think rspec is a great example of a completely unnecessary DSL. The TLDR, for me, is a DSL is a new thing to learn. But the base ruby language is very flexible and expressive. So the tradeoff of getting some slightly “prettier” language that is “customized to your domain” is rarely worth the overhead of the new mini-language. It’s fundamentally optimizing for the wrong thing: localized syntactic concerns over global simplicity. Another way of saying the same thing: it’s taking on one more dependency you don’t need.

                    1. 5

                      I don’t even think most of Active Support is monkey patching. Monkey patching is replacing or extending existing functionality. What Active Support does is extend core classes. The author slides from one into the other as if there is no distinction. Since ruby allows monkey patching it is more precarious to extend core classes. You could effectively accidentally monkey patch a core class if that core class has a method of the same name added to it. Extending core classes to provide a dialect isn’t the same thing though. Lots of languages that forbid monkey patching allow extending core classes for the very purpose of facilitating dialects. The most extreme example is probably LINQ.

                      1. 3

                        People in the Ruby community use the word to refer to both kinds of reopening classes. There isn’t a distinction other than to acknowledge that extending APIs is less bad than patching existing behavior.

                    2. 2

                      I strongly agree with author and strenuously avoid monkey-patching and DSLs in ruby.

                      So do I - as I say all I disagree with is his thesis that it isn’t Ruby. It is! And until refinements it was idiomatic Ruby, too, even if it was something to be avoided wherever possible.

                  2. 13

                    I’m surprised that the article doesn’t mention Ruby’s refinements feature. That feature, introduced in Ruby 2.0, provides a safer alternative to monkey-patching by limiting the monkey-patch to an explicit lexical scope. I would liken using a refinement to opting into a dialect only for a small piece of code, which I think would not be much riskier than using a non-monkey-patching library that provides a DSL.

                    With refinements, a library could mimic ActiveSupport’s 1.day.ago API without adding methods to numbers in the rest of the project:

                    def user_trusted_yet?(user)
                      using SomeRefiningDateLibrary
                    
                      user.signup_time < 1.day.ago
                    end
                    
                    1. 13

                      Limited lexical scope is exactly why it would never be adopted by Rails. You’d have to add using ActiveSupport or whatever to every single file in your app. This goes right the face of “no boilerplate” thesis of Rails philosophy.

                      It also can not be use in a method scope. Your example wouldn’t work. This is another confusing thing about refinements.

                      Refinements are not hygienic either. This means that every refinement can pollute global objects with instance variables, for example. This is probably a pragmatic choice as refinements can smuggle some state between lexical scopes but this kinda defeats the isolation aspect of the feature.

                    2. 10

                      I don’t often work with Ruby or RoR, but when I do I constantly mix up which features are Ruby-native and which ones are only present in RoR. This does not happen in Django, for example, where the code itself is without a doubt a clear Python, even if it’s wrapped in layers of abstractions. So I agree with an author even if I find the title a little bit clickbait‘y.

                      1. 7

                        In the same vein, the Linux kernel is not written in C. It immediately becomes apparent when you read and write kernel code. This has many reasons, for example that Linus Torvalds thinks that the ISO C Standard is a “piece of garbage” that imposes “braindamage” which needs to be undone [1] and also because the kernel uses non-standard GNU C extensions [2]. Think of that what you will, I guess it works well enough to power most devices on the planet. Makes you wonder if the Linux way of writing C should be the actual C standard.

                        [1] https://lkml.org/lkml/2018/6/5/769

                        [2] https://www.kernel.org/doc/htmldocs/kernel-hacking/conventions-gnu-extns.html

                        1. 11

                          I think that’s somewhat different. Rails is using Ruby to implement an embedded DSL that is quite Ruby-like. Linus is using a language that is mostly the same as C, ignoring what the standard says, and then complaining that security vulnerabilities in his kernel are someone else’s fault when they arise from the compiler implementing the standard.

                        2. 6

                          This reminds me of the old prototype JavaScript framework. Back in the day, it was a serious contender for becoming the frontend framework of choice. It did many of the tasks that jQuery or UnderscoreJS do.

                          It took the idea of JavaScript having prototypical inheritance and ran with it - extending core object prototypes (mostly of DOM objects, but also Array and even Object) with useful shorthand methods. For example, you could write Document.getElementById('foo').className += ' bar' as $('foo').addClassName('bar'). The $ is not like in jQuery where it returns a special proxy object that operates on the DOM objects it points to, but literally just a shorthand for Document.getElementById. (well, not 100% the same, because IE didn’t support manipulating prototypes of DOM objects…) It also allowed you to write [1, 2, 3].map(function(x) { return x * x; }) to square numbers in an array, back when such functional idioms were mostly unheard of in JS.

                          I think it fell out of favour when jQuery hit the scene because it did much of the same but in an even more concise way, even if it didn’t rely on extending objects in the original (Self-inspired) spirit of the language. I found the idea quite elegant, but in retrospect I can certainly see (potential) problems with it when Prototype’s added methods would clash with ones from additional (Prototype-unaware) libraries or with native methods.

                          Basically, the problem with approaches like Prototype and Rails is that they’re polluting the “global” namespace of methods defined on core objects. It’s not exactly as introducing global variables or methods, but it comes close to it, as they’re adding methods to object types they don’t own (now referred to in the derogatory term “monkey patching”). Also, it’s no wonder the Rails authors originally baked in Prototype as the frontend library for dynamic UI stuff - it shares the same ethos.

                          1. 4

                            The thing is also that Prototype got “swallowed” by the ECMAScript specs. Nowadays most of the functions that were part of the Prototype are now part of the JS itself, so the library itself became pointless.

                            1. 3

                              A lot of the things are in ECMAScript now, but it seems to me that Prototype could’ve continued to be developed in the same direction as underscore. There was also scriptaculo.us (a UI library built on top of Prototype, with the same idea) and other such extensions, but this seems to have died out too.

                            2. 3

                              Yeah, extending prototypes is largely discouraged in Javascript communities and this article does a great job of explaining why it’s a bad idea.

                            3. 6

                              I think that’s a great way of putting it. While Ruby isn’t my major language and RoR isn’t my framework of choice, I maintain a RoR project and when other developers interact with it it’s hard to explain, when they mix up RoR and Ruby a lot.

                              For some of them I think RoR gave them a very bad opinion about Ruby. So while it might be that Rails in a way made Ruby popular, it feels like it also put people off trying to use Ruby. I noticed that once when I realized how hard it was to find maintained libraries outside of the real of web frameworks. At least some areas felt like an abandoned ghost city.

                              I think Ruby picks up a lot of interesting concepts from Smalltalk in a language that is more accessible to how we usually develop things today. A lot of them are really missing, everywhere else.

                              Anyways, it feels like we switched from that class of languages being maybe over-used to not using them anymore, yet sometimes end up hacking in bits here and there in things that are usually called frameworks, so where enough code accumulates that it is considered okay. Those could be considered dialects or accent as well.

                              What I want to say with all of that is that a lot of languages gravitate into the same direction, making them more similar, which might be nice for switching, but I think is overall a bad thing, because then having various tools with different strengths and weaknesses turn into tools of trends and hypes, mostly described by which decade they stem from, like fashion.

                              Overall it seems like a lot of reinventing the wheel and having to keep up with fashion trends in order to find jobs, or other developers and a reinforcement of the newer is better “logic”.

                              1. 3

                                What I want to say with all of that is that a lot of languages gravitate into the same direction, making them more similar, which might be nice for switching, but I think is overall a bad thing, because then having various tools with different strengths and weaknesses turn into tools of trends and hypes, mostly described by which decade they stem from, like fashion.

                                Finally, someone with some sense ;) I’ve been saying this for a while but people always think I’m against “progress”. Some things just fit certain languages better than others, and you don’t want all languages to converge to the same point, as that would give you no real reason to choose one language over the other.

                              2. 4

                                I don’t know that the conclusion follows the opening. Dialects evolve to fit a distinct group of people as the needs of those people diverge from the general population. Contrast this with a domain’s jargon which is extra terminology to apply to a specific problem set that some speakers of the language may have. In the programming world this is frameworks and libraries. Frameworks each have their own particular way of speaking the language, and libraries can add extra vocabulary in to any framework. In terms of natural language, if I already speak the Beijing dialect of Chinese I will have an easier time adjusting to the Sichuan dialect than a non-Chinese speaker. And a speaker of either dialect can learn the jargon required for Computer Science. In terms of programming, if I already know the React dialect of JS, I will have an easier time learning Vue than a non-JS programmer. And I would also posit that React and Vue have both been a step up from vanilla JS for the needs of application building. Though vanilla JS has grown and improved during the time those projects have existed and the dialects in the future may weaken

                                1. 3

                                  Lack of scope, a properly module/import system is what makes Ruby harder than it looks.

                                  Imagine you open a file and see this code

                                  https://apidock.com/rails/v6.1.3.1/Class/class_attribute

                                  class Base class_attribute :setting end

                                  https://apidock.com/rails/Module/mattr_writer

                                  module HairColors mattr_writer :hair_colors end

                                  What do you do ? Literally somones just google class_attribute for Ruby docs, only to found it in Rails doc and confuse? Where does class_attribute come from? If it’s Python we have to do something like from .. import foo as and they know where it comes. In Ruby, it pops up out of no where, populate the current scope where you see it like a walk in the part.

                                  So to me, the problem is that Rails tried to mimick a lot of core Ruby method. mattr_writer to mimick attr_wrtiter ?

                                  If it was written this way

                                  class Base Rails.class_attribute :setting end

                                  everyone will be happy

                                  1. 3

                                    Reading all the comments about wanting to make a dialect or whatnot within a language and no mention of Forth, where you can define essentially anything and even redefine words within scopes! Just how real world languages can have the same words/sounds but different meanings.

                                    1. 2

                                      The article makes a lot of good points. Monkey-patching is dangerous and a bad practice for libraries. Randomly patching core classes can very quickly turn into a massive headache. In my opinion, the ruby community has largely learned this lesson in this regard. It was used far too much a decade ago.

                                      But the rest of the article falls flat. ActiveSupport is special and it does have a monopoly… on being ActiveSupport.

                                      This is because ActiveSupport isn’t a library, it’s a language extension, and it is so widely used that is has become a standard on its own. Rails Ruby is its own language. You might as well complain that python has a monopoly on being python.

                                      Overall, this is a very, very old argument. I started with Rails 1.2 in 2007 and saw this on IRC. Some years later, I remember going to a ruby meetup in Seattle in 2011. There were some very angry people there that hated Rails for ruining their language. And it was clear this was going on for years before I ever started programming.

                                      1. 5

                                        It’s bad for Ruby because it ends up being an embrace-and-extend strategy that damages alternatives.

                                        People get so used to the dialect, that you have to choose between allowing Rails to be the gatekeeper of the entire language, or else be marginalized.

                                        1. 2

                                          I’ve never used Rails or Ruby, so as an outsider I assume then that merging some version of ActiveSupport into the main Ruby codebase was a non-starter for various reasons? Was there any effort to merge in enough support into the base Ruby that monkey-patching was no longer needed?

                                          1. 11

                                            Some things from ActiveSupport are copied into Ruby eventually. Things like 1.day.ago won’t because they add time functions to the integer class, which is counter to the style of Ruby.

                                          2. 2

                                            This is a failure of compositionality. When you use a general-purpose programming language, an implicit premise is that you can take any two or more libraries written in it, and incorporate them in your program. Whereas using libraries written in other languages might require using a FFI, serializing objects to a shared data store, or in general putting more effort into setting up a bridge between the languages.

                                            The fact that a library happens to implement a language extension doesn’t somehow reduce this expectation. The extended language is supposed to be compatible with the old one - that’s what being an extension means.

                                            This is an area where I think Racket is miles ahead of the competition (including Common Lisp!), namely, making user-defined language extensions play nicely with each other.

                                          3. 2

                                            I wrote my first ruby program in over 15 years last week. all my ruby experience predated rails, and I hit this situation almost immediately with my new program.

                                            1. 1

                                              This is why namespaces.

                                              1. -1

                                                In Poland we speak Polish, which is a really difficult language, it’s actually considered one of the top-10 most difficult languages to learn in the world.

                                                I get annoyed with this kind of comment.

                                                I bet if you ask the Chinese, English is the hardest language to learn in the world. Yet it’s the language most widely spoken around the world.

                                                I don’t know what kind of ethnic pride comes into play when people say “my language is difficult” or “my language is easy”. Usually they just mean the orthography matches closely the pronunciation or not. For example, most “English is difficult” jokes come from its spelling; almost no English speakers even complain about phrasal verbs or even know about them, and that’s arguably a far less predictable aspect of spoken English.

                                                Polish is really easy to learn if you know, say, Russian. Polish is extremely easy to learn if you’re a Polish child. I don’t know by what kind of metric one could reasonably classify the 10 most difficult natural languages of the world.

                                                1. 5

                                                  It’s not entirely out of thin air. What he’s referring to is the list given out by the Foreign Service Institute, which lists polish as one of the harder languages (more granular lists exist, frequently indeed posting it in the top 10). https://www.state.gov/foreign-language-training/

                                                  Adding nuance to that would break the storytelling.

                                                  1. 4

                                                    The assumption of that list is you are starting from English. “Hardness” is relative to knowing some first language, not generalizable.

                                                    1. 4

                                                      There are also metrics like the average age when a child knows their mother tongue proficiently, based on that metric you can see that some languages are indeed very hard.

                                                    2. 1

                                                      What he’s referring to

                                                      This seems like an over-application of the principle of charity. I don’t feel that charitable. I’m sure there’s some list out there that puts Polish in the top 10, but I put little faith in it.