News for hackers. Stuff that matters.
Re:Rails is not written in Ruby by overgard on Friday February 04, 2022 @05:22PM (#30214188)
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.

Re:Rails is not written in Ruby by mmahemoff on Friday February 04, 2022 @05:31PM (#30214304)
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.

Re:Rails is not written in Ruby by jonnycat on Friday February 04, 2022 @05:44PM (#30214486)
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.

Re:Rails is not written in Ruby by omoikane on Friday February 04, 2022 @06:03PM (#30214724)
Re:Rails is not written in Ruby by leifg on Friday February 04, 2022 @06:41PM (#30215135)
If I understand the Ruby pipeline operator it's essentially an alias of the dot. The example in the feature tracker is:

``` 1.. |> take 10 |> map {|e| e2} |> (x) ```

which is equivalent to

``` (1...).take(10).map{|e| e2} ```

So in order to use it you still need extensions on every single object. The only reason that looks nice is because in this example you continuously call methods on lists that return a list. You can't add `my_custom_list_operation(list_input)` in between.

Re:Rails is not written in Ruby by WallowingInShit on Saturday February 05, 2022 @10:31AM (#30222028)
Same thing, but for JavaScript: https://github.com/tc39/proposal-pipeline-operator

Going forward, the pipe operator is going to be showing up in many new languages. I've used elixir a lot, and the pipe operator is a genius piece of syntactic sugar.

Re:Rails is not written in Ruby by dragonwriter on Friday February 04, 2022 @07:10PM (#30215414)
It's worth noting that that version of pipeline operator was abandoned, while there are still (some newer) open feature requests for a more traditional functional-style pipeline operator.
Re:Rails is not written in Ruby by tylerscott on Friday February 04, 2022 @09:38PM (#30216519)
yield_self is an less elegant way to achieve basically the same thing as |>.
Re:Rails is not written in Ruby by jon1628492 on Friday February 04, 2022 @06:03PM (#30214722)
Or alternatively,

    some_array
    |> do_thing()
    |> do_another_thing()
    |> do_something_else()
Re:Rails is not written in Ruby by amw-zero on Saturday February 05, 2022 @01:54AM (#30218524)
Now replace |> with . And you have the previous code example. The difference is completely superficial.
Re:Rails is not written in Ruby by jonnycat on Saturday February 05, 2022 @07:28AM (#30220380)
The end result is superficially different. That's the point - we like the flow of the end result! But the implementation is completely different, as it's pure functional compared to object oriented.

"string".do_stuff() - do_stuff() is a method on the "string" class, with all of the potential gotchas that might come with an OO implementation. Does the method mutate the internal state of the object? Does the particular class override the default implementation? Is it a monkey-patched implementation (the issue raised by this article)?

"string" |> do_stuff() - do_stuff() is a standalone pure function. The data is immutable and does exactly what it says.

Re:Rails is not written in Ruby by machiaweliczny on Saturday February 05, 2022 @06:40AM (#30220162)
It's not because you can use any function with pipeline and with dots chaining you can use only methods returned by previous function call.
Re:Rails is not written in Ruby by kazinator on Saturday February 05, 2022 @01:17PM (#30223938)
That is false; you cannot chain functions that do not return a value that is suitable as an argument to the next function.

a.this().that() requires that a.this() returns an object on which .that() can be invoked.

(that (this a)) requires that (this a) returns an object that is a suitable argument to that.

Re:Rails is not written in Ruby by kazinator on Saturday February 05, 2022 @01:15PM (#30223914)
Not completely, because the . has a meaning: select a member/field/slot from a class object.
Re:Rails is not written in Ruby by cuillevel3 on Friday February 04, 2022 @06:19PM (#30214905)
"I bacon eat"

Ruby syntax apparently is especially nice if you know Japanese: https://flatironschool.com/blog/ruby-and-the-japanese-langua...

Re:Rails is not written in Ruby by empthought on Friday February 04, 2022 @05:38PM (#30214411)
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

Re:Rails is not written in Ruby by funklute on Friday February 04, 2022 @05:48PM (#30214531)
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).
Re:Rails is not written in Ruby by dragonwriter on Friday February 04, 2022 @07:51PM (#30215784)
> But this kind of functional composition often doesn’t have first-class support in the language syntax.

Are we talking about Ruby? Because it does.

It has both:

  (do_thing >> do_another_thing >> do_the_last_thing)[arg0,…,argn]
And:

  (do_the_last_thing << do_another_thing << do_thing)[arg0,…,argn]
(Where the things in the composition are procs/lambdas, the closest thing Ruby has to functions as distinct from methods.)
Re:Rails is not written in Ruby by funklute on Saturday February 05, 2022 @04:34AM (#30219456)
> Are we talking about Ruby?

I wasn't thinking specifically of Ruby (and indeed I don't know Ruby, nor was I aware that it has this kind of support).

Rather, I was thinking about programming languages in general, since the top-level comment seemed to be talking about the general principle.

Out of 5 languages I use on a regular basis, only one of them has _some_ support for function composition. But even then, not as flexible as what you're demonstrating here. So my point is that across programming languages, the chaining approach is generally more readable than the composition approach. And that in turn favours methods over global functions.

If I ever start using Ruby, I would probably be on the fence about which approach is better though.

Re:Rails is not written in Ruby by samatman on Friday February 04, 2022 @07:54PM (#30215809)
He did in fact, you're using points to represent (ironic!) the point-free style, prefix style and circumfix style, they're all function composition.

Which, believe it or not, they still teach function composition as good old g(f(x)) in high school algebra...

Re:Rails is not written in Ruby by wtetzner on Friday February 04, 2022 @07:26PM (#30215586)
Shouldn't it be the other way around?

    (do_something_else . do_another_thing . do_thing) some_array
Re:Rails is not written in Ruby by timr on Friday February 04, 2022 @05:44PM (#30214472)
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.

Re:Rails is not written in Ruby by choward on Friday February 04, 2022 @05:38PM (#30214400)
So it's a purely a syntax issue? Other languages like elm have a cleaner syntax for that.
Re:Rails is not written in Ruby by vlunkr on Friday February 04, 2022 @06:36PM (#30215094)
Well you can't write your backend in elm, so that's not much help.
Re:Rails is not written in Ruby by choward on Saturday February 05, 2022 @12:36PM (#30223480)
That's why I said other languages. Apparently elixir has that feature too.
Re:Rails is not written in Ruby by bryanrasmussen on Friday February 04, 2022 @05:54PM (#30214615)
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.

Re:Rails is not written in Ruby by greiskul on Friday February 04, 2022 @06:27PM (#30214976)
Programming language syntax matters. It's the interface in which the programmer writes the code, synthetic sugar can reduce the "mental overload" of a feature.

For example, in C, the array operator is mostly synthetic sugar for pointer addition, and could be removed. Should it be removed? Of course not, cause programmers are not thinking of pointer arithmetic when indexing an array.

And as much as I love lisp, it's one of the top reasons none of them ever had widespread adaption. Nobody likes having to do math like this (* (+ (sqrt 4) 2) 4).

Re:Rails is not written in Ruby by didibus on Friday February 04, 2022 @09:44PM (#30216572)
There's a lot of advantages to having the Lisp syntax, especially with code navigation and editing. I personally much prefer it.

And because of easy macros (another advantage of the syntax), you can easily add syntactic sugar as you see fit where you care.

Re:Rails is not written in Ruby by vlunkr on Friday February 04, 2022 @06:37PM (#30215100)
While composability is nice, I've always assumed they were just really into OO ideas when they wrote active support.
Re:Rails is not written in Ruby by Twisol on Friday February 04, 2022 @05:56PM (#30214635)
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 add any more after the fact. So at some level you're forced to use 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".

Re:Rails is not written in Ruby by greiskul on Friday February 04, 2022 @06:38PM (#30215108)
As someone that used to have to program in Java, moved over to Kotlin, but now have to work with Java again... I miss extension methods so much. Util classes feel so hacky, and are so ugly to use. Specially for cases like streams, where you are invoking a chain of operations, and then have to store the temporary result in a variable, or have worse readability.

Or for quick and dirty debugging, with print statements, being able to put a println in the middle of any method call, just by adding a .also{println(it)} to the chain.

Re:Rails is not written in Ruby by Twisol on Friday February 04, 2022 @06:46PM (#30215172)
One pattern I see sometimes is to define a util class, but rather than having a bunch of static methods, you wrap the underlying object in a util wrapper that adds all of the utility methods. Unfortunately, Java also has no good way to delegate methods without simply redefining all of them, so it's by no means a perfect solution. (And it's not really worthwhile unless you've got a fluent API that returns the same object every time; otherwise you're wrapping for just one call, which is arguably noisier.)
Re:Rails is not written in Ruby by int_19h on Friday February 04, 2022 @06:27PM (#30214980)
The most straightforward solution to this is the one that D employs, whereby A(B) is syntactic sugar for A.B() if the former is not otherwise valid.
Re:Rails is not written in Ruby by dragonwriter on Friday February 04, 2022 @06:36PM (#30215087)
> The most straightforward solution to this is the one that D employs, whereby A(B) is syntactic sugar for A.B() if the former is not otherwise valid.

Isn't the D solution actually that:

  A.B(...)
is syntactic sugar for:

  B(A,...)
if A.B() is not specifically defined.

(Yours both flips the elements and reverse the direction of sugaring, as I understand it.)

Re:Rails is not written in Ruby by kazinator on Saturday February 05, 2022 @01:09PM (#30223837)
The first example is three characters longer and dispatches methods under the hood. There could easily be a dozen do_thing, do_another_thing and do_something_else methods.

In the second example, we have only function application. Put a breakpoint or print statement into do_another_thing and it is sure to go off.

(do_another_thing could support OOP dispatch as a generic function. A generic function is still something that is called first then dispatches methods based on its arguments. E.g. in Common Lisp we can (trace do-another-thing) to trace the calls.)

Re:Rails is not written in Ruby by TomVDB on Friday February 04, 2022 @05:30PM (#30214294)
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.

Re:Rails is not written in Ruby by dfinninger on Friday February 04, 2022 @05:37PM (#30214383)

    len(my_list)
is just sugar for

    my_list.__len__()
if you feel strongly about the convention: https://docs.python.org/3/reference/datamodel.html#object.__...
Re:Rails is not written in Ruby by int_19h on Friday February 04, 2022 @06:29PM (#30215007)
It's not just sugar; it also does things like ensuring that the value is actually a nonnegative integer.

And for other unary "magic methods", the disconnect can be even bigger. E.g. iter(x) is not just a synonym for x.__iter__() - it can also produce an iterator for something that only provides __len__ and __getitem__.

Re:Rails is not written in Ruby by dragonwriter on Friday February 04, 2022 @07:57PM (#30215830)
This is, I think, largely a result of Python not idiomatically using mixins as much as Ruby (since it has multiple inheritance, it supports them, and they are sometimes used, but Ruby leans into them hard in the core.)

In the Ruby way, with otherwise similar setup, you would have a mixin that provided __iter__() given __len__() and __getitem__(), and classes that had the last two but wanted the behavior of the first would just include that mixin class as a parent, rather than having a separate branch in iter() to handle the different cases. (Ironically, its one way that Ruby is less TMTOWTDI than Python is.)

Re:Rails is not written in Ruby by progval on Monday February 07, 2022 @05:31PM (#30252320)
> In the Ruby way, with otherwise similar setup, you would have a mixin that provided __iter__() given __len__() and __getitem__(),

Python does have such a mixing, it's collections.abc.Sequence https://docs.python.org/3/library/collections.abc.html#colle...

Re:Rails is not written in Ruby by RangerScience on Friday February 04, 2022 @05:44PM (#30214471)
+1 on the unpredictability being an issue. "Only one right way to do", yeah... gimme that least surprise :)
Re:Rails is not written in Ruby by tyingq on Friday February 04, 2022 @06:10PM (#30214804)
I understand why Python does it this way, but this is usually where someone points out Ruby doing:

  somestring=somearray.join(' ')
Versus Python's:

  somestring=' '.join(somearray)
Re:Rails is not written in Ruby by dragonwriter on Friday February 04, 2022 @06:17PM (#30214872)
I generally like Ruby better than Python, but Python's choice on _join_ being a string method on the thing stuck between elements rather than an collection method on the things whose elements are getting something stuck between is better.
Re:Rails is not written in Ruby by renox on Friday February 04, 2022 @06:33PM (#30215047)
Why do you think it's better?

I don't use either Ruby or Python but I think that the first way is more natural.

Re:Rails is not written in Ruby by renewiltord on Friday February 04, 2022 @06:37PM (#30215102)
Interesting. I model `array.join(',')` or the equivalent Py `','.join(l)` in my head as ultimately (pseudo-code):

    foldLeft(tail(array), str(head(elem)), (acc: String, elem: T) -> acc + ',' + str(elem))
Which makes the array method easier to understand than the string method. Funny how I thought it was _obvious_, but you find the opposite obvious.
Re:Rails is not written in Ruby by mcphage on Saturday February 05, 2022 @12:05AM (#30217731)
Why? somearray is the important piece, the string is just a bit of spacing. Put the important thing first, don’t bury the lede.
Re:Rails is not written in Ruby by pmontra on Saturday February 05, 2022 @02:29AM (#30218709)
After years of Python I can't remember which one of these are correct

  1 ",".join(["a", "b"])
  2 ["a", "b"].join(",")
  3 ",".split("a,b")
  4 "a,b".split(",")
These method names feel like active form so 1 and 3 should be correct but it's 1 and 4 despite "a,b" obviously is not splitting "," but it's splitted by it

In Ruby it's 2 and 4.

I'm out of luck twice but at least Ruby has a and b always to the left and the comma always to the right so I always remember it.

Re:Rails is not written in Ruby by tedunangst on Friday February 04, 2022 @07:13PM (#30215451)

    print('Hello '.join('world!'))
Re:Rails is not written in Ruby by drusepth on Friday February 04, 2022 @07:41PM (#30215718)
The former's inherent namespacing is exactly why I prefer it. You know exactly where each method came from (it's a method that exists in a `some_array` instance namespace); inversely, I have no idea where `has_my_thing(foo)` comes from or what all it can be used for if it's just sitting in the global namespace.

Very often, when I'm learning new code, there's one line I'll write over an over in console bindings:

    > some_object.methods
It provides a super-easy way to learn what methods are actually relevant to `some_object` without having to pour over every method available in the global namespace and figure out whether `some_object` is a valid argument to each. You can also apply set logic like `some_object.methods - {}.methods` to return only the methods available on `some_object` that AREN'T on typical objects, which is a very, very, very powerful learning/exploration tool.

For example,

    irb(main):003:0> PaypalService.methods - Service.methods
    => [:create_prepay_invoice, :build_client, :capture_invoice_funds, :get_order_info, :get_plan_price]
Side note: you can also call `.source_location` on any method to see exactly what file/line it's defined in (even in core ruby/rails methods). There's a lot of other inspection methods available to learn more about objects and what you can do with them, which IMO is a benefit over your latter example.
Re:Rails is not written in Ruby by nitrogen on Friday February 04, 2022 @09:40PM (#30216527)
Along these lines, I recommend also trying Pry's ls command, which breaks the methods down by which module or class implements them for a given object, and the show-source command.

It's this great ease of introspection that makes Ruby so fun.

Re:Rails is not written in Ruby by sobellian on Friday February 04, 2022 @05:51PM (#30214567)
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!

Re:Rails is not written in Ruby by toomanydoubts on Friday February 04, 2022 @06:27PM (#30214986)
Why not this? (sqrt . sum . map square . fft) list

I really don't see what chaining via methods bring that simple function composition doesn't.

Re:Rails is not written in Ruby by dragonwriter on Friday February 04, 2022 @07:59PM (#30215859)
> I really don’t see what chaining via methods bring that simple function composition doesn’t.

Visual flow from input to output.

(Ruby can compose either way, so you can build a pipeline of procs in flow order, but if you aren’t chaining the input still goes at the end, not the beginning.)

Re:Rails is not written in Ruby by Mehdi2277 on Friday February 04, 2022 @07:57PM (#30215833)
That's not possible to write in most languages. If a language does support compose operator or custom operators then yes I think that style is good too. But there is no way to write that style in python/ruby.

My ideal preference is for a language to support universal function call syntax where x.f(y) is equivalent to f(x,y) always and also include support for custom operators (or at least include common functional operators).

Re:Rails is not written in Ruby by dragonwriter on Friday February 04, 2022 @08:02PM (#30215890)
> That’s not possible to write in most languages. If a language does support compose operator or custom operators then yes I think that style is good too. But there is no way to write that style in python/ruby.

Ruby has composition operators in both directions:

given:

  x = 2
  f = lambda {|a| a+1}
  g = lambda {|a| a*2}
the following are all equivalent:

  f[g[x]]
  (f>>g)[x]
  (g<<f)[x]
What’s inconvenient is that to use it with methods, even ones that can be invoked in ways that look like function calls in other languages, you have to realize a method object, since method names don’t name objects. Or, as Matz says (I don’t think of Ruby as a Lisp at all, but in this context it makes sense) Ruby is a Lisp-2, not a Lisp-1.
Re:Rails is not written in Ruby by pmontra on Saturday February 05, 2022 @02:44AM (#30218802)
Those square parentheses are unfortunate because traditionally they are used to index arrays. I'd like to write one of

  2.g.f
  g(2).f
  (g >> f)(2)
  (f << g)(2)
with a strong preference for the first one. That's the Elixir pipeline with a dot instead of a |>

The second one is OK-ish but it turns weird with a long list of functions.

The last ones are not nice to read, too much syntactical overhead.

There is a ton of new syntax added to Ruby in the last years and they had to find a way to fit it into the space between what was already there. The results are not always nice IMHO.

Re:Rails is not written in Ruby by dragonwriter on Saturday February 05, 2022 @05:06AM (#30219667)
> Those square parentheses are unfortunate because traditionally they are used to index arrays.

They’ve been among the methods for invoking procs since at least Ruby 1.8 (2003); there are several – all of these are equivalent.

  f[x]
  f.(x)
  f.call(x)
  f.yield(x)
> I’d like to write one of:

  2.g.f
  g(2).f
  (g >> f)(2)
  (f << g)(2)
Ruby is a pure OOP language, and “.” is the syntax for method invocation. So its really not available as a composition operator. So the first two don’t work for that reason. Further extending Ruby’s syntax to make unmodified parens both invoke methods and serve as syntax sugar for .call() on objects would probably be problematic (that’s why “.()” was adopted.)

> There is a ton of new syntax added to Ruby in the last years

None of the stuff related to this except the composition operators is added recently. And those aren’t really syntax, just normal operator methods added to the Proc class.

Re:Rails is not written in Ruby by mcphage on Saturday February 05, 2022 @12:07AM (#30217742)
Passing other parameters into the composed functions.
Re:Rails is not written in Ruby by Mikeb85 on Friday February 04, 2022 @05:26PM (#30214237)
> 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.

Re:Rails is not written in Ruby by zer0-c00l on Friday February 04, 2022 @05:34PM (#30214337)
Except here the encapsulation comes at the cost of extending a core library at runtime, which has all the tradeoffs mentioned in the article.
Re:Rails is not written in Ruby by jshen on Friday February 04, 2022 @05:39PM (#30214413)
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.

Re:Rails is not written in Ruby by dragonwriter on Friday February 04, 2022 @06:27PM (#30214984)
> Except here the encapsulation comes at the cost of extending a core library at runtime

No, it doesn't.

Pervasive OO style and monkey-patching core classes are orthogonal. Ruby favors the former strongly, and supports the latter, and Rails rather heavily leverages the latter (to the extent that it has been harshly criticized for it by much of the non-Rails Ruby community), but there is no necessary relationship between them.

Re:Rails is not written in Ruby by zer0-c00l on Friday February 04, 2022 @09:17PM (#30216385)
“Here” in my comment was referring specifically to Rails’ use of the latter method, sorry for the confusion
Re:Rails is not written in Ruby by matsemann on Friday February 04, 2022 @05:46PM (#30214513)
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)
Re:Rails is not written in Ruby by dgb23 on Friday February 04, 2022 @05:34PM (#30214346)
The level of encapsulation seems to be the same. It’s just a different notation in that case.
Re:Rails is not written in Ruby by WallowingInShit on Saturday February 05, 2022 @10:36AM (#30222079)
has_my_thing(some_array) can be encapsulated too, using modules. For example, in elixir you have functions like List.to_string(some_list) and Map.values(some_map). This way, you get encapsulation and don't have to have a class.
Re:Rails is not written in Ruby by servytor on Friday February 04, 2022 @05:31PM (#30214310)
Because intellisense can help.
Re:Rails is not written in Ruby by geysersam on Friday February 04, 2022 @05:48PM (#30214530)
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?

Re:Rails is not written in Ruby by samatman on Friday February 04, 2022 @07:56PM (#30215823)
Easily, for typed languages: the values which can be parameters are constrained to values which are in scope and of the appropriate type.
Re:Rails is not written in Ruby by MadcapJake on Friday February 04, 2022 @09:51PM (#30216621)
Both styles can utilize that kind of intellisense on parameters. What about code completions before you've specified the parameters? Not really possible in the functional style unless your functions are namespaced and then you're still limited to what was in that module. Only with object-verb method passing can you get code completions related to the data before you've started specifying (the rest of) the parameters.

When declaring a variable, in a typed language you could use the variable type to filter down the possible function completions based on return type; in practice, I don't think any LSPs do that.

Re:Rails is not written in Ruby by RangerScience on Friday February 04, 2022 @05:42PM (#30214455)
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.

Re:Rails is not written in Ruby by sparker72678 on Friday February 04, 2022 @05:27PM (#30214260)
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.
Re:Rails is not written in Ruby by ajmurmann on Friday February 04, 2022 @05:40PM (#30214434)
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.
Re:Rails is not written in Ruby by ratww on Friday February 04, 2022 @07:18PM (#30215516)
> The former, the extension has so many issues, it messes up namespacing, it's unclear where it came from, etc. etc

That depends on the implementation. A monkey-patch is definitely problematic, but when it's properly scoped, like C# extensions, Rust Traits or Ruby Refinements, it's quite elegant.

Re:Rails is not written in Ruby by dinoreic on Friday February 04, 2022 @06:02PM (#30214708)
Because it is baked into the class itself. Ruby is global variables "fiesta", because all classes and modules (and functions that begin with first uppercase letter) and became global once loaded.

Because of that, you can not have 2 parallel versions on the same lib, in the sane app, not possible. You load classes once, and they are accessible everywhere, magic! No need for loader headers as in JS, Python, Java, Go, ...

That said, ruby devs like to have "sane" extensions like to_json. People proffer to write "@object.to_h.to_json" instead of "JSON.parse(@object.to_h)" and not to think about loading json lib. I agree that Rails is abusing this too much.

Re:Rails is not written in Ruby by jaynetics on Friday February 04, 2022 @06:11PM (#30214810)
> you can not have 2 parallel versions on the same lib, in the sane app

with the very latest ruby version, you actually can:

https://rubyreferences.github.io/rubychanges/3.1.html#kernel...

Re:Rails is not written in Ruby by lloeki on Friday February 04, 2022 @06:39PM (#30215117)
I suppose you're referencing:

    Kernel#load: module as a second argument
(the link jumps to some random place for me)

Well, you always could, it was just a bit more involved:

https://github.com/lloeki/pak

Re:Rails is not written in Ruby by ajmurmann on Friday February 04, 2022 @05:29PM (#30214281)
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.

Re:Rails is not written in Ruby by inopinatus on Friday February 04, 2022 @06:49PM (#30215204)
A better interface might be

    my_thing.in?(some_collection)
since it affords the object agency to decide for itself.
Re:Rails is not written in Ruby by dgut on Friday February 04, 2022 @08:33PM (#30216118)
Because not all programmers are made equal.

For some

  method(thing a, thing b) thing {
    return a * b
  }
is not better than

  method
    a * b
  end
If you're an "artist" type that care about the aesthetics of your code you tend to prefer the second.
Re:Rails is not written in Ruby by fernandotakai on Friday February 04, 2022 @05:24PM (#30214216)
>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.

Re:Rails is not written in Ruby by matheusmoreira on Friday February 04, 2022 @05:32PM (#30214318)
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.

Re:Rails is not written in Ruby by remram on Friday February 04, 2022 @05:51PM (#30214564)
Nothing like sort is hidden in class methods in Python.
Re:Rails is not written in Ruby by johnmaguire on Friday February 04, 2022 @05:55PM (#30214620)
That's not true - Python implements sorted by calling `__lt__` on the object: https://docs.python.org/3/library/functions.html#sorted
Re:Rails is not written in Ruby by int_19h on Friday February 04, 2022 @06:37PM (#30215097)
__lt__ is just the way to overload the comparison operator, which of course is going to be called by the sort. What OP is saying is that there's no __sort__ or anything like that.
Re:Rails is not written in Ruby by johnmaguire on Friday February 04, 2022 @07:43PM (#30215738)
But that's not really true either - there's __len__ which len() calls: https://docs.python.org/3/reference/datamodel.html#object.__...

You can add iterators with __iter__, reversed() calls __reversed__, etc.

Re:Rails is not written in Ruby by dwaite on Friday February 04, 2022 @08:09PM (#30215940)
Sorted mandates the type you give it implements __iter__, aka that it is iterable. Ruby defines sort as a method on Enumerable.

The difference is that most likely that CPython and CRuby started with different capabilities to map native implementations into the language.

The biggest difference between the two is python adds more functions into the global namespace, while Ruby pushes pretty hard to encapsulate logic within types.

Re:Rails is not written in Ruby by remram on Saturday February 05, 2022 @02:41PM (#30225028)
The discussion is about how to provide additional functionality.

To be sortable, you collection has to be a collection, and it has to have comparable elements. That has to be provided by the type itself.

The difference between Python and Ruby is how to add more functions. If you make a library to sort collections (or format phone numbers), the Ruby way is to monkey-patch collections (or numbers). Whereas the Python way is to offer a free function that takes an iterable (or number). This is what we are discussing.

Of course Python classes have methods.

Re:Rails is not written in Ruby by matheusmoreira on Friday February 04, 2022 @06:02PM (#30214712)
No, pretty much everything in the language is hidden from you behind methods. This hidden magic is in many cases literally baked into the language's syntax.

Iterables use functions like __len__ and __next__. The with keyword is just a thing that implicitly calls __enter__ and __exit__ on a context management object.

Re:Rails is not written in Ruby by remram on Saturday February 05, 2022 @02:16PM (#30224735)
Yes, sorted() will use methods like __iter__ and __lt__ provided by the object. But sorting is not provided by the object and is therefore not (quoting GP) "the exact same thing, just hidden from you in an ugly __method__".
Re:Rails is not written in Ruby by dragonwriter on Friday February 04, 2022 @06:48PM (#30215195)
> Nothing like sort is hidden in class methods in Python.

The discussion has mostly been about instance methods defined in classes, and how do you define “like sort” in this claim?

list.sort() is a method in Python; it's the equivalent of Array#sort! in Ruby.

Re:Rails is not written in Ruby by remram on Saturday February 05, 2022 @02:30PM (#30224894)
There is nothing that sorts hidden in dunder methods on objects, even if they can be sorted. `sorted()` will use intrinsic methods like `__iter__` and `__lt__` to sort a collection, but the op is claiming that even though the syntax is different (`sorted(obj)` rather than `obj.sort!`), it's "the exact same thing, it just hides it from you using ugly __methods__".

To that I reply that no, there is no method to sort hidden in __methods__. This is not how functionality is added to classes in Python. If you want to add something that works on all iterables, you don't insert new methods in iterable classes, like you do in Ruby. That free-standing sorted() function really is a free-standing function, it is not actually a method "hidden in ugly __methods__".

Re:Rails is not written in Ruby by dragonwriter on Saturday February 05, 2022 @08:54PM (#30228483)
> If you want to add something that works on all iterables, you don't insert new methods in iterable classes, like you do in Ruby.

You don't in Ruby, either.

If you want something that works on all Enumerables, you normally write an (instance or class/module) method on some other class or module that takes an argument that supports the methods provided by the Enumerable API.

(In Python, you might do this or write a bare function, but Ruby doesn't have bare functions; it's possible to make proc objects in a library which is the nearest equivalent, but you’d almost never do that, because module methods are much more convenient.)

In either Python or Ruby you might also monkey-patch existing classes to add functionality to the., and this is slightly more common in Ruby than Python, but because Python iterable, unlike Ruby Enumerables, don't have a common superclass that isn't shared with non-iterables, you couldn't easily do a “for all iterables” thing in Python [0] by monkey-patching because you don't have a good target to monkey-patch, whereas in Ruby you might do something for (nearly, because duck typing is a thing and it's possible someone could build an enumerable by implementing the whole API without mixing in the module) all enumerables, not by adding methods directly to individual enumerable classes, but by monkey-patching the Enumerable module itself.

The main difference here is that Python (despite having support for mixins because it uses multiple inheritance) tends in it's core to use a mix of OO public APIs and procedural public APIs built on top of OO protocols using (often dunder) methods, rather than either consistently-OO or consistently procedural APIs, whereas Ruby is consistently OO and uses mixins to provide shared functionality building on a narrower protocol where Python would use a suite of functions. (Python tends to be more OO in its stdlib and third party libs than in its core, except specifically when supporting protocols defined in the core and consumed through built-in functions.)

[0] except by monkey-patching object with code that tries to use the dunder methods that underlie the iteration protocol and fails if they don't exist, but while theoretically possible I don't think anyone would do that.

Re:Rails is not written in Ruby by remram on Sunday February 06, 2022 @04:14PM (#30237065)
The Python ecosystem does not monkey-patch, and in fact a lot of built-in classes do not support it.

Your point about Ruby syntax not really being meant for free functions is well-taken though.

Re:Rails is not written in Ruby by dwaite on Friday February 04, 2022 @08:02PM (#30215889)
> one of the reasons i prefer python to ruby. sorted(my_thing) will sort anything that can be sorted. no need for monkey patching

sort() in ruby is declared on Enumerable. Sorted() in python is declared to take an iterable first parameter. They are pretty much equivalent, except python has built-in functionality to reverse the order of the output array, while ruby you would have to negate your sort function.

Ruby however allows types to override sort, such as when the underlying type is a red black tree which is already sorted. For duck typing, you can also declare a compatible sort method on types which do not implement Enumerable - doing so in python would require a separate method.

Re:Rails is not written in Ruby by int_19h on Friday February 04, 2022 @06:38PM (#30215110)
sorted() will return a new collection. You still need to do foo.sort() in Python if you want to sort in-place.
Re:Rails is not written in Ruby by drusepth on Friday February 04, 2022 @07:57PM (#30215836)
Don't mind me, just adding the equivalent ruby code for readers to compare:

    array.sort # returns a new collection

    array.sort! # sorts in place
(Ruby convention is to add a ! when methods directly modify the object they're called on.)
Re:Rails is not written in Ruby by dragonwriter on Friday February 04, 2022 @10:01PM (#30216677)
> Ruby convention is to add a ! when methods directly modify the object they're called on

No, Ruby convention is to add a “!” when there is a “less safe” version of an operation, for which a “more safe” version exists without a “!”.

Mutating versions of methods where a nonmutating version also exists are one common example, but not the only one, and mutating methods without a nonmutating counterpart don't get “!”.

Re:Rails is not written in Ruby by runevault on Friday February 04, 2022 @05:26PM (#30214241)
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).
Re:Rails is not written in Ruby by jacobsenscott on Friday February 04, 2022 @05:52PM (#30214584)
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.
Re:Rails is not written in Ruby by lamontcg on Friday February 04, 2022 @06:54PM (#30215272)
I have to maintain code that was written by a developer who has long since left the space. He monkeypatched in stuff like Hash#dig years before it landed in ruby with a slightly different API and blew up the codebase. Since he was a pretty clever developer and had some wide experience in other languages he would monkeypatch in useful stuff from other languages -- but by doing that collisions with the core language are actually pretty frequent. Many people have the same clever idea when its really just copying some pattern that was successful in some other language.

Awhile back I gutted the code and ripped out 60% of it, including all the monkeypatching and the maintanence burden on every ruby release dropped sharply.

(And I'm assuming we're talking about the more polite kind of Monkeypatching where you're adding methods to global classes that are net-new, not the practice of opening up a class and scribbling over methods that you didn't write and replacing behavior, where should you should always mental picture your SemVer contract being lit on fire)

Re:Rails is not written in Ruby by RangerScience on Friday February 04, 2022 @05:46PM (#30214512)
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.

Re:Rails is not written in Ruby by manquer on Friday February 04, 2022 @05:27PM (#30214250)
The first style is more aligned to how some people reason the statement in their mind and that is why they like it better.
Re:Rails is not written in Ruby by almog on Friday February 04, 2022 @06:16PM (#30214864)
I guess it comes down to how we are used to process natural language, in which the noun comes before the verb.