Live data from Hacker News

Some Smalltalk about Ruby Loops

tech.stonecharioteer.com

41–50 of 59 posts

Re: Some Smalltalk about Ruby Loops

#41
post #5

> But Python also looks up methods at runtime. Does that mean Python also does message passing? Not quite. I don't think Ruby's "message passing" is fundamentally different from Python's "method calls". Ultimately, both languages implementations are very similar: both look up methods by name in a hash table and then call them. IMO "message passing" is just an alternative metaphor for what Ruby does when you type `obj…

> This means that in Ruby, `object.name` is always calling a method defined on `object.class`, with `self` set to `object`. That can be re-interpreted as "sending a message" to `object.class`. Ruby Instances can have their own methods and override methods separate from their class.

More precisely, Ruby instances each have a (empty initially) “singleton class” that comes before their “regular” class in method resolution order, and to which methods can be added.

Re: Some Smalltalk about Ruby Loops

#42
post #10

I think this misses the point. `times` is "better" than `for` because it's declarative, reads like English, etc. Which of course are opinions, but the implementation details (messaging passing or not) are irrelevant. Example: Swift and Kotlin can do `Int#times` and don't need message passing to get it done.

> but the implementation details (messaging passing or not) are irrelevant.

I'm the author here. While I agree that the implementation details do not matter for _most_ developers, I wanted to learn Ruby with an understand of how things are implemented.

In my previous post[1], I go into why I've always felt I didn't need Ruby since I knew Python, and I spent time learning Rust instead. It felt redundant to learn a second dynamically typed programming langauge that offered no advantage in performance. I started working at Chatwoot, where we use Ruby, and I had to pick up Ruby for the job. I didn't want to be satisfied with a "user-level" knowledge of the language and instead wanted to rip its internals apart so I can learn why it does the things it does.

Call it a preference on how I want to learn things.

[1] - https://tech.stonecharioteer.com/posts/2025/ruby/

Re: Some Smalltalk about Ruby Loops

#43
post #25

Leaving the comfort of lisp, I have started to wonder: why are all the looping facilities in other languages so awful? Standing on the shoulders of giants, I made this little abomination: https://rikspucko.koketteriet.se/bjoli/goof-loop It handles 98% of all loops I write, meaning I don't have to manage state (even for things like treating things like circular data). I find it removes many of the stupid errors I make…

How is that different from select map and reduce in ruby?

You can iterate over strings and lists and arrays at the same time. You can do destructuring. You can accumulate values in every part of the loop, so if you have subloops you can accumulate values in the outer and inner and every intermediate loop and most important: it builds no intermediate results meaning it will be much faster.

Re: Some Smalltalk about Ruby Loops

#44
post #38
post #36

Earlier quoted context omitted.

Yet, iterating over several collections and accumulating values is awkward. It is not that it is easy to implement the iteration protocol, it is that the construct for looping universally sucks. Even the awful loop macro from common lisp runs laps around most other facilities. Racket's is even less powerful, but is miles better than what python offers. Ruby can do some destructuring, but not enough to actually be use…

It may be somewhat awkward; zipping collections to iterate over them in unison makes the first one (the sender) seem special, while it not necessarily is. And accumulating more than one value isn't always straightforward; you usually have to reduce explicitly. But it's so much better in the general case! Like the example you showcase your macro with. In Ruby it would be: lst = [[1, 2], :dud, [3, 4], [5, 6]] lst.grep(…

Your example of the flatten and grep will build two intermediate arrays before summing. That's inefficient.

I would also reach for higher order functions for most things, but when you need things to be fast that is not how you would do things. You would express it with nested for loops and ifs, and you would have to juggle state all by yourself. That is my main critique of looping constructs: they only cover the absolute simplest case, and then you are by yourself.

The partition example is not a particularly nice implementation of partition. It is there to show that you can write non-tail recursive loops, meaning you can use it to do tree walking and such. Using mutation, like your example, would make it prettier, but would make the code unsafe with schemes call/cc. Even a traditional named let (tail recursion) would be prettier.

Re: Some Smalltalk about Ruby Loops

#45
post #22

Earlier quoted context omitted.

If you want OOP than, yes, Elixir isn't it... maybe Pony? Curious what else you don't like about Elixir though besides not being OOP... it's definitely got messaging!

A robust static type system is what was missing for me, at least last time I looked.

[deleted]

Re: Some Smalltalk about Ruby Loops

#46
post #22

Earlier quoted context omitted.

If you want OOP than, yes, Elixir isn't it... maybe Pony? Curious what else you don't like about Elixir though besides not being OOP... it's definitely got messaging!

A robust static type system is what was missing for me, at least last time I looked.

Static typing and messaging (OOP) don’t exactly fit together. If you want OOP, you are fundamentally beholden to dynamic typing.

Re: Some Smalltalk about Ruby Loops

#47

I like this article a lot but if you want to dig deeper and understand some of the negative consequences of Ruby's approach, you might like these two articles I wrote: https://journal.stuffwithstuff.com/2013/01/13/iteration-insi... https://journal.stuffwithstuff.com/2013/02/24/iteration-insi...

Oh this looks good. I'm the author of the original post. Thanks for this. And thanks for reading.

Re: Some Smalltalk about Ruby Loops

#48
post #28

Love Ruby. Wish I could use it more often. Always end up reaching for Python in day-to-day because of how mature and well-docunented the libs are. Anyone who likes this kind of stuff, highly recommend Metaprogramming Ruby [2] by Paolo Perrota. Great look into Ruby innards and inspiring code examples. Gets me pumped

Author here.

That's one of the books I'm reading now!

Re: Some Smalltalk about Ruby Loops

#49
post #43

Earlier quoted context omitted.

How is that different from select map and reduce in ruby?

You can iterate over strings and lists and arrays at the same time. You can do destructuring. You can accumulate values in every part of the loop, so if you have subloops you can accumulate values in the outer and inner and every intermediate loop and most important: it builds no intermediate results meaning it will be much faster.

Yes, you can do all that in ruby, plus you can choose to do it lazily such that chaining operations uses generators or by copy at any point so maybe this is a classic case of lispbian hubris.

Re: Some Smalltalk about Ruby Loops

#50
post #44
post #38

Earlier quoted context omitted.

It may be somewhat awkward; zipping collections to iterate over them in unison makes the first one (the sender) seem special, while it not necessarily is. And accumulating more than one value isn't always straightforward; you usually have to reduce explicitly. But it's so much better in the general case! Like the example you showcase your macro with. In Ruby it would be: lst = [[1, 2], :dud, [3, 4], [5, 6]] lst.grep(…

Your example of the flatten and grep will build two intermediate arrays before summing. That's inefficient. I would also reach for higher order functions for most things, but when you need things to be fast that is not how you would do things. You would express it with nested for loops and ifs, and you would have to juggle state all by yourself. That is my main critique of looping constructs: they only cover the abso…

For the record, if you want to avoid the creation of intermediate arrays, you can:

    lst.lazy.grep(Array).flat_map(&:itself).sum
Not as clear, because the standard library doesn't have a `#flatten` method for lazy enumerators.

But the point is the interface, not the implementation. Efficiency doesn't mandate assembly-like DSLs. Your interface could be as clear and clean as Ruby's and produce fast, inlined code by the power of macros. Ruby doesn't have macros, so chaining lambdas is the best it can do.

Ruby also has call/cc. None of the iterating methods has any provision to make them "safe" from it. They aren't safe from modifications to the iterated collection either. I think it makes sense; being forced to accumulate using only linked lists and having to be constantly on guard to support a rarely used quirky feature is a bad tradeoff IMO.

Post reply on HN