[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)
 
web link (solnic.codes)
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)