[HN Gopher] Rails is not written in Ruby
       ___________________________________________________________________
        
       Rails is not written in Ruby
        
       Author : tanyar
       Score  : 75 points
       Date   : 2022-02-04 21:47 UTC (1 hours ago)
        
 (HTM) web link (solnic.codes)
 (TXT) w3m dump (solnic.codes)
        
       | hprotagonist wrote:
       | "and behind every framework, there's a langauge trying to get
       | out, to protect semantic ideas, then semantic ones, and _then_
       | support the logical ideas that we have " -- Lambda World 2019 -
       | Language-Oriented Programming with Racket - Matthias Felleisen
       | 
       | https://youtu.be/z8Pz4bJV3Tk?t=180
        
       | TazeTSchnitzel wrote:
       | Rust's traits let you add methods like this to any type without
       | creating a risk of conflicts, because they aren't accessible
       | within a particular file unless you import the trait. (Also I
       | think there's some syntax for disambiguating?)
        
         | remram wrote:
         | The syntax is function-style, e.g. `Trait::method(object)`
         | rather than ` use Trait; object. method()`
         | 
         | Or you can cast &object to &Trait.
        
       | overgard wrote:
       | So like, I guess the thing I don't understand is this: why do
       | people think writing:
       | 
       | some_array.has_my_thing() is better than
       | has_my_thing(some_array). ?
       | 
       | The former, the extension has so many issues, it messes up
       | namespacing, it's unclear where it came from, etc. etc. The
       | latter is just a function. It's not longer, or complex, it's just
       | more straightforward. I really think things where you extend or
       | override built in types is almost always just a bad idea, because
       | the alternative is almost always just as concise and doesn't
       | confuse everyone.
        
         | RangerScience wrote:
         | In your example, `has_my_thing` then has to be able to handle
         | any possible input (even if it's just to say "I can't handle
         | this input). Example: length of objects/hashes, and length of
         | arrays.
         | 
         | And, sure, I guess you _could_
         | `Array.has_my_thing(some_array)`... but that 's just re-
         | arranging the mixin structure behind `Array`. It only looks
         | different until you start peaking under the hood.
         | 
         | IMHO, sometimes you want a function because in your problem
         | domain there's a universal (or universal enough)
         | act/question/etc, and sometimes you want a member function
         | because in your problem domain the act/question/etc only makes
         | sense given some particular context.
        
         | fernandotakai wrote:
         | >some_array.has_my_thing() is better than
         | has_my_thing(some_array). ?
         | 
         | one of the reasons i prefer python to ruby. sorted(my_thing)
         | will sort anything that can be sorted. no need for monkey
         | patching -- you just call it and, if it can be sorted, it will
         | be sorted.
        
           | runevault wrote:
           | Did they ever fix the arbitrary nature of monkey patching
           | where last in wins? I've not touched ruby in years (and never
           | seriously after I read up on how prevalent MP was).
        
             | RangerScience wrote:
             | Sort of. They introduced "refinements" as the thing you're
             | supposed to do instead of monkeypatching, but AFAIK it
             | doesn't get wide-spread usage.
             | 
             | IME you don't usually do monkey-patching, although there
             | are circumstances where it makes A LOT of sense (usually
             | test suites). It's one of those sharp knives - safe if you
             | plan out using it, dangerous if you don't.
        
             | jacobsenscott wrote:
             | Yes, they added refinements several years ago. That said
             | I've been programming rails for probably 10 years, and I
             | never see anyone use refinements, and I can count on one
             | hand the number of times monkey patching has actually been
             | a problem for me personally. I'm sure there are cases where
             | it bit someone hard, but this is one of those things that a
             | theoretically bad, but never seem to really have any impact
             | in practice.
        
           | matheusmoreira wrote:
           | In Ruby, anything that can be sorted will respond to sort. So
           | thing.sort will also sort anything that can be sorted.
           | 
           | Python does the exact same thing, it just hides it from you
           | using ugly __methods__ that the language itself will call by
           | convention. What determines sortability? "Anything that can
           | be sorted" just means anything that implements the __lt__
           | method. In Ruby, this method is called <=> and the Comparable
           | module builds upon it.
        
             | remram wrote:
             | Nothing like sort is hidden in class methods in Python.
        
               | johnmaguire wrote:
               | That's not true - Python implements sorted by calling
               | `__lt__` on the object:
               | https://docs.python.org/3/library/functions.html#sorted
        
         | Mikeb85 wrote:
         | > So like, I guess the thing I don't understand is this: why do
         | people think writing: > some_array.has_my_thing() is better
         | than has_my_thing(some_array). ?
         | 
         | The latter is in the global namespace (or you have to create a
         | module/namespace for it). The former is encapsulated in a
         | class. Encapsulation is nice.
        
           | zer0-c00l wrote:
           | Except here the encapsulation comes at the cost of extending
           | a core library at runtime, which has all the tradeoffs
           | mentioned in the article.
        
             | jshen wrote:
             | One benefit is discoverability. I coded a ton of Ruby/rails
             | back in the day and it was nice to be able to jump into a
             | repl and see what methods were on an object. Also, you
             | could often find the source of the method through the repl.
             | 
             | I think a lot of these debates come down to repl
             | development versus ide development. I've done a lot of
             | both, but many complaints come down to people on one side
             | not understanding the way the other side works.
        
             | matsemann wrote:
             | In other languages (like Kotlin), that extension is
             | actually not done on the object, but is a statically
             | imported function and it's only syntactic sugar. This
             | avoids all the pit falls (no collision, knows where the
             | function comes from etc)
        
           | dgb23 wrote:
           | The level of encapsulation seems to be the same. It's just a
           | different notation in that case.
        
         | manquer wrote:
         | The first style is more aligned to how some people reason the
         | statement in their mind and that is why they like it better.
        
         | sparker72678 wrote:
         | Personally, I just find `some_array.has_my_thing?(thing)` to be
         | more readable, and I spend way more time reading code than
         | writing it.
        
           | ajmurmann wrote:
           | I think this might be hosting complexity which reminds me a
           | little of Rich Hickey's Simple Made Easy talk. The Rails way
           | seems more readable because it's hiding complexity. The other
           | approach reveals the complexity, but actually has less
           | complexity than the Rails style solution. This might only
           | become apparent when something goes wrong.
        
         | ajmurmann wrote:
         | I think you are right that the solution where you pass the
         | array in is clearer and probably more maintainable. It doesn't
         | seem object-oriented at all though. It also leads to a totally
         | different look and feel between built-in and extensions. It's
         | not idiomatic at all.
         | 
         | So, I think it's fair to say that the advantages you are lay
         | out are more tangible than the other side of the trade-off.
         | 
         | Having written too much Ruby code I'd still cringe every time I
         | have to pass the array.
         | 
         | Edit: To make it actually clean it should be
         | 'MyArrayLib.has_my_things(array)'. Still really dislike it
         | despite all the logical arguments for it.
        
         | TomVDB wrote:
         | I'm switching back from Ruby to Python out of necessity, and I
         | loath things like len(my_list) vs my_list.len().
         | 
         | I just don't understand why Python thinks it necessary to
         | define global functions for something that naturally should
         | exist as a method.
         | 
         | And, of course, there's no way to know up front when to use a
         | global function vs when to use a method.
        
           | RangerScience wrote:
           | +1 on the unpredictability being an issue. "Only one right
           | way to do", yeah... gimme that least surprise :)
        
           | dfinninger wrote:
           | len(my_list)
           | 
           | is just sugar for                   my_list.__len__()
           | 
           | if you feel strongly about the convention: https://docs.pytho
           | n.org/3/reference/datamodel.html#object.__...
        
         | mmahemoff wrote:
         | Composability.
         | 
         | some_array.do_thing().do_another_thing().do_something_else()
         | 
         | versus
         | 
         | do_something_else(do_another_thing(do_thing(some_array)))
         | 
         | Note the first example could be an example of chaining - where
         | each function returns "self" and we progressively transform the
         | object. Or it could be simply each function returning different
         | values which are operated on. Either way, the pattern is
         | usually cleaner and easier to debug than the nested equivalent.
        
           | choward wrote:
           | So it's a purely a syntax issue? Other languages like elm
           | have a cleaner syntax for that.
        
           | empthought wrote:
           | It's super ironic that you used the term "composability" and
           | didn't actually use functional composition for the second
           | example.
           | 
           | (do_thing . do_another_thing . do_something_else) some_array
        
             | funklute wrote:
             | But this kind of functional composition often doesn't have
             | first-class support in the language syntax. That is, it
             | often requires you to rely on helper functions or
             | libraries. As a result, it's not nearly as readable or
             | intuitively obvious as the chaining example (and this is in
             | contrast to mathematical notation, where functional
             | composition is very clear, in my opinion).
        
           | timr wrote:
           | You don't need to implement chaining for X.foo() to make
           | sense. It's object-oriented syntax. If anything, languages
           | like Python are the odd ducks here, mixing magical globals
           | (len(X) => X.__len__()) with plain-old OO syntax conventions.
           | 
           | That Ruby _also_ implements X.foo().bar().baz() is a weird
           | thing about Ruby that can be very powerful, but is unrelated
           | to the dot syntax.
           | 
           | I feel like roughly half of Ruby programmers treat it as
           | "Lisp but better", which leads to confusion about the OO
           | aspects of the language.
        
           | jonnycat wrote:
           | Elixir (and some other languages as well) solves this with
           | the pipe operator:
           | 
           | some_array |> do_thing() |> do_another_thing() |>
           | do_something_else()
           | 
           | Even though Elixir is functional and aggressively not object
           | oriented, if you squint at this pattern you get the
           | readability of an OO-style method chaining API.
        
           | [deleted]
        
           | bryanrasmussen wrote:
           | I'm pretty sure they're both equally composable as I
           | understand the term for programming language usage, it's just
           | that you prefer the first way of writing it.
           | 
           | Admittedly I don't think anyone really prefers the second way
           | of writing it, but that's not the same as being ecstatic over
           | the first one.
        
           | Twisol wrote:
           | A now-deleted (v_v) comment rightly called out that in many
           | languages, method-style invocations are inextensible: a fixed
           | set of methods exists when the class is created, and you
           | can't any more after the fact. So at some level you're
           | _forced_ to switch to direct-style invocations, and mixing
           | the two can become ugly.
           | 
           | Some languages (like C# and Rust) support extension methods,
           | so you _can_ use method-style to invoke separately-defined
           | procedures. But in other languages, method-style APIs become
           | an inextensible privileged zone, and I personally prefer to
           | minimize the number of inherent methods as much as possible.
           | By preferring direct-style invocations, I avoid the
           | temptation to stick a useful method on the class itself  "for
           | convenience".
        
         | servytor wrote:
         | Because intellisense can help.
        
           | geysersam wrote:
           | That makes sense. This is the most convincing reason I've
           | come across in this thread.
           | 
           | How do code completion work in languages where the predicate
           | goes before the subject?
        
         | sobellian wrote:
         | Subjectively, the chief advantage is readability for long
         | chains of function composition. Compare
         | list.fft.map(square).sum.sqrt to sqrt(sum(map(fft(list),
         | square))). This is all a matter of taste, but IMO the former
         | reads more easily.
         | 
         | It's a pretty trivial matter, so I wouldn't go breaking
         | encapsulation everywhere just to support it. D has a feature
         | called Universal Function Call Syntax where a.f(b, c) is sugar
         | for f(a, b, c) for any free function, rendering those issues
         | moot. I just wish more languages would pick it up!
        
       | spyckie2 wrote:
       | I think this is the beauty of Ruby. The core philosophy of Ruby
       | is that it was designed to make "1.day.ago" possible. That's why
       | DHH fell in love with it and used it to build Rails. You can't do
       | that with many other languages.
       | 
       | "1.day.ago" is not really a piece of code, it's a sensibly named
       | shortcut to existing code that already solves easy problems. It's
       | is not designed to be built upon; not all code has to be designed
       | that way.
       | 
       | For example:
       | 
       | // How to get last day of month?
       | 
       | // Javascript
       | 
       | var month = 0; // January
       | 
       | var d = new Date(2008, month + 1, 0);
       | 
       | console.log(d.toString()); // last day in January
       | 
       | // monkey patching in ActiveSupport
       | 
       | Date.today.end_of_month
       | 
       | This is the definition of a great DSL. It abstracts away easy
       | problems so you don't have to solve them hundreds of times and
       | allows you to focus on the more unique problems of a domain.
       | 
       | Monkey patching is a powerful tool and just like any powerful
       | tool it should be used carefully. Random monkey patching
       | everywhere is bad, just like polluting the global namespace in
       | javascript is bad. But used well and you can create extremely
       | expressive, powerful, and concise coding experiences. Isn't that
       | the goal of any DSL?
        
         | matsemann wrote:
         | Think that should be possible in Kotlin as well, fwiw.
         | Extension functions and extension properties are nice. And
         | function with receivers make for some nice DSLs. All without
         | monkey patching, which I consider a better way.
        
       | sparker72678 wrote:
       | ActiveSupport is one of my very favorite things in the Ruby
       | ecosystem.
       | 
       | I can understand why it might not be to someone's taste, but I
       | find the idioms to be generally delightful to use and, in most
       | cases, extraordinarily convenient.
       | 
       | But, as always in the Ruby ecosystem, if you don't like it, find
       | the thing that brings you joy!
        
       | ativzzz wrote:
       | I think the author's example of time_calc compared to
       | ActiveSupport's 1.days.ago syntax speaks to why ActiveSupport is
       | used so much and that sometimes it's OK to do the wrong thing to
       | make everything else easier. The time_calc API looks just awful
       | to use I'd rather deal with the implications of potential Ruby
       | "magic".
        
       | mmahemoff wrote:
       | You can make that argument if you consider a language is more
       | than just syntax. It's an complicated ecosystem, of tooling
       | (compilers, runtimes, repls, IDEs, debuggers), reusable code
       | (libraries, frameworks), conventions (coding styles, idioms,
       | design patterns), and community.
       | 
       | I'm not really fussed about dictionary definition debates anyway.
       | I generally use ActiveSupport in personal Ruby projects. It just
       | adds too much value to ignore.
        
       | RangerScience wrote:
       | > RSpec is our primary example here - a limited and problematic
       | DSL based on monkey-patching was turned into a beautiful,
       | composable DSL which we still have in RSpec.
       | 
       | One of my most consistent experiences with Ruby is doing
       | something the "wrong way" - but being _able_ to do, due the
       | language flexibility - and then later coming back to the problem
       | and realizing the better solution, which ends up being more
       | satisfactory and happier in every way.
       | 
       | Ruby lets you do whatever you want, however you want, but it
       | makes the _right_ way the most satisfying. Very handy for
       | iterative development.
        
       | byroot wrote:
       | I think "Jargon" would probably have been a much less
       | controversial term than "Dialect" for expressing this idea.
       | 
       | "Dialect" implies that it's a sub-language, but Ruby is still
       | Ruby no matter how many methods you add or modify.
       | 
       | Whereas a "Jargon" is a specialized vocabulary, and even
       | sometimes some word change meaning as part of a jargon.
       | 
       | But I suppose that when you title your post like this,
       | controversy is actually what you are looking for.
        
       | ecf wrote:
       | Clickbait title
        
       | anandvc wrote:
       | ActiveSupport is a core part of Rails though.
        
         | steve_adams_86 wrote:
         | I think this is the author's take on that:
         | https://solnic.codes/2022/02/02/rails-is-not-written-in-ruby...
         | 
         | Not sure this is what you're getting at. I agree with the
         | author though, this aspect of Rails bothered me quite a bit.
         | Knowing that working on a Rails project meant I could magically
         | generate ranges from dates because of ActiveSupport was
         | unsettling - it made me wonder what else was non-native
         | behaviour and waiting to trip me.
        
           | jon-wood wrote:
           | I just can't bring myself to get worked up about it - I've
           | done a lot of Rails, and I've done a lot of pure Ruby. You
           | quite quickly learn which bits come from ActiveSupport when
           | you step outside Rails because... it doesn't work. It's not
           | going to trip you up in 8 month's time after shipping your
           | code, you're just going to get an exception when you first
           | try to run it, and then either implement it the pure way or
           | add a dependency on ActiveSupport.
        
       | manquer wrote:
       | Prototype modification of global objects in JS provides similar
       | monkey patching capability. Interestingly evolution is very
       | different though
       | 
       | The instances of abuse are generally less in mainstream JS
       | libraries, and in many of them such features are usually opt-in
       | or at-least it is possible to still use the lib even when opting
       | out , there is also no major dialect that has privileged rights
       | so to speak.
        
         | choward wrote:
         | The Prototype js library was pretty huge back in the day when
         | monkey patching was all the rage.
        
       | hihihihi1234 wrote:
       | As a former Rails dev, I've always enjoyed Piotr Solnica's
       | writing. He does a great job of explaining what's wrong with the
       | framework, in particular the flaws that aren't immediately
       | obvious when you're starting out.
       | 
       | But these days the main thing I think of when I hear his name is
       | that his blog used to be on a different domain, and he apparently
       | let the old registration expire because someone else has bought
       | it and now it points to a porn site. And tragically, there are
       | still many other sites online which still link to Solnica's
       | outdated, now-pornographic URLs.
       | 
       | And that's how I ended up accidentally sharing a porn link in a
       | professional setting.
        
         | widdershins wrote:
         | Somehow this story seems to relate to the author's musings on
         | conflicting monkey-patched libraries. It's all rather
         | beautifully messy.
        
       | temac wrote:
       | And programs written in Lisp are not written in Lisp?
        
       | jaredcwhite wrote:
       | I understand that if you like Ruby a whole lot and like being a
       | web developer, but you _don 't_ like Rails for whatever reasons,
       | it can be a frustrating experience over the long haul.
       | 
       | But as a counter-example to that, I'm constantly amazed that
       | there _are_ a ton of Ruby gems out there which pretty much work
       | as advertised outside of Rails (even if Rails gets a privileged
       | happy path during config). Not to mention you can use ecosystem
       | stalwarts like Puma, Rack, Sidekiq, and many other subsystems
       | outside of a Rails app entirely. Heck, you can pull just bits of
       | Rails in if you really need them. Want Active Record for your ORM
       | but nothing else? That 's totally possible! (But of course then
       | you get a bunch of Active Support stuff too which is what the
       | author objects to.)
       | 
       | Personally I _love_ Active Support and actively (heh) bring it
       | into my Ruby projects if it 's not there already. To a certain
       | degree, everything people say is a "bug" about Ruby's
       | metaprogramming/monkeypatching is a feature in my book. The fact
       | that "Ruby core" is simply a substrate upon which you can build
       | your own flavor of a Ruby-plus language--be that "The Rails Way"
       | or something else entirely--is amazing. In that respect, I
       | entirely disagree that Rails is not written in Ruby. Rails shows
       | us how you _can_ (and probably should!) use Ruby to build DSLs
       | which suit your specific application/framework purposes very
       | well. It's not a bug. It's a feature. And it's why most other
       | tools which claim "Rails but for Language X" fall short...they
       | can try to replicate features of Rails, sure--but because it's
       | not Ruby, it misses the whole point of using Rails, which is that
       | you get Ruby "for free" to sweeten the deal! That's the (not so)
       | secret sauce here.
        
       ___________________________________________________________________
       (page generated 2022-02-04 23:00 UTC)