Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This was definitely written by a pythonist! If I tried to write it, as a rubyist, I'm sure I'd get some things about python wrong. (I find it notable how few people there are that are actually familiar with both).

The standard alternative to `for` in ruby does involve `each` and blocks... but definitely doesn't involve defining a custom `each` method on your class... That is a specialty thing that most developers have probably done rarely. Let alone defining it in terms of `for`, which is just weird!

But the basic principle stated "Instead of passing data back to the for loop (Python) you pass the code to the data (Ruby)" -- is more or less accurate.

blocks -- syntactic support in the language for cleanly passing a single in-line defined lambda/closure object as an argument -- are possibly the thing that are most special to ruby.

> Python builds on for-like constructs for all kinds of processing; Ruby pushes other kinds of data processing work to methods.

OK, maybe, although not totally sure what you mean.

> Ruby keeps going with its methods-first approach, except instead of each we have a new set of methods commonly implemented on collections, as below:

Um. I'm not sure where the author is getting this. It is certainly possible, as shown, but definitely not common to implement `select` or `map` or other methods provided by Enumerable directly on your custom class. It is a bit more common to implement `each` alone and let the `Enumerable` mixin provide the rest. But even that I'm not sure how "common" I'd call it.

> Ruby, however, inverts this. Ruby puts object-orientation as the foundation of the pyramid. Ruby contains the messy procedural world in blocks, letting objects work with those procedural blocks.

OK, true. The author is getting the details wrong, but I guess their overall picture is still right?



> blocks -- syntactic support in the language for cleanly passing a single in-line defined lambda/closure object as an argument -- are possibly the thing that are most special to ruby.

Too bad ruby stopped short of doing the trivial obvious thing and just making blocks be regular values. Instead the language is complicated by special syntax and functions for sending and receiving blocks, and bizarrely limited by the inability to do anything with a block literal other than send it.

Blocks were so close to being good. They managed the triple flip with a double twist, but they couldn't stick the landing. It's not quite a faceplant at the end, but it clearly shows how much better they could have been.


What do you mean by inability to do anything with a literal? You can capture it, turn into a variable, turn it into a "lambda" (make next, break, return local), use it like callbacks are used in any other language. Blocks are a bit special in that they go in their own slot for message sends (method calls) which allows the syntax to be unambiguous and the yield keyword allows for optimized calls to passed in blocks because methods that don't reference a block as data don't have to worry about the block outliving the stack frame.


No there’s special methods which turns blocks into objects, and there’s a syntax to do that, but blocks aren’t objects unless you specifically do one of those things. The idea you’re replying to is that they’d be objects always.


  def proc &p
    p
  end
is what the special method looks like but yeah they’re not standalone expressions but rather part of the method call syntax.


> Too bad ruby stopped short of doing the trivial obvious thing and just making blocks be regular values.

Abstractly, I kind of see the point, in practice, I don't see it makes much difference, and given Ruby’s two flavors of function-like objects, it seems to work out for the best.

> the inability to do anything with a block literal other than send it.

But sending lets you do anything else you’d want to to do with it. Specifically, to get either of the flavors of callables, you pass it to “proc” or “lambda”, and then use the result.


Not having every block be a separate value makes it easier for the Ruby VM to optimize such code without escape analysis (which is something that's pretty hard to do in this language).

Don't have to allocate the container => don't need to use the heap one more time. Nor access the block's code through that indirection.

Same thing with methods: they aren't objects, but you can create an object pointing to a method any time you need one.


You have that with procs and lambdas


> Too bad ruby stopped short of doing the trivial obvious thing and just making blocks be regular values. Instead the language is complicated by special syntax and functions for sending and receiving blocks, and bizarrely limited by the inability to do anything with a block literal other than send it.

Blocks are syntactical structures. Your statement is like saying that the parens and commas that are part of the argument list should be "regular values".

I'm not sure what you want to do with a "block literal". If you want to do something with the closure represented by the block, then reify the block into an object to pass it around, wrap it, introspect etc.

I think a lot of confusion about blocks in Ruby is really inconsistent terminology. Using "block" to mean the syntactic expression of a closure, i.e. part of the method call syntax I think helps to disambiguate the syntactical nature of closures from the closure reified into an object that you can pass around, call, etc. (i.e. an instance of the Proc class)


> Blocks are syntactical structures. Your statement is like saying that the parens and commas that are part of the argument list should be "regular values".

Well, no. Know what else is a syntactic structure? Numeric and string literals. People would never have touched the language in the first place if Ruby required you to write code like

    x = String("abc")
    y = Integer(123)
But people bend over backwards to explain why

    z = { |x| x + 1 }
Is bad and shouldn't be allowed.

What?

The whole block vs proc thing is an artificial distinction. It doesn't add value, it removes it.

(Yeah, I know about the difference in how they handle return. This strikes me as an incredibly ad-hoc way to address something they could have solved a lot more elegantly.)


> But people bend over backwards to explain why “z = { |x| x + 1 }” is bad and shouldn't be allowed.

Really? I’ve never seen anyone bend over backward to explain it as bad, mostly just that that's the way it is, there are tradeoffs either way, and its not worth changing.

> The whole block vs proc thing is an artificial distinction

Presumably, you mean lambda vs. proc. (Both of which are defined using blocks, and procs are what using & syntax in a function signature causes a passed block to be reified into.)

But, yes, all distinctions are creations of humans, especially all distinctions within human creations like, say, programming languages. So “artificial distinction” is a meaningless descriptor when we are talking about things in a programming language.


Would it be fair to say you don't want to write this?

  z = ->(x) { x + 1 }

  z.call(y)


I don't see why there is this distinction where lambdas have to be called differently than functions.


Ruby doesn't have functions. It only has methods. That blocks in MRI happen to be implemented without reifying an object is an optimisation. You can only ever obtain a reference to said block by reifying it into a Proc instance.

Letting you obtain some kind of raw reference to a block that isn't a method on an object would make blocks unlike every other value in Ruby.


> I don't see why there is this distinction where lambdas have to be called differently than functions.

Ruby doesn't have functions; things that look like bare (non-method) function calls in other languages are just method calls on self.

So procs/lambdas can't be called the same as, or differently from, functions—they are the closest thing Ruby has to functions to start with.


Hah, as a Pythonista, I'd say it was definitely written by a Rubyist :D

That initial example would usually be, in Python:

  class Stuff:
    def __init__(self):
        self.a_list = [1,2,3,4]
    def __iter__(self):
        for value in self.a_list:
            yield value
Basically, iterators and generators are native constructs that for loop operates on in Python (lists, which are actual "data", are simply special-case optimized instances of those).

Instead of calling iterators/generators "data", I'd call them wrapper control-flow constructs that `for` really operates on which provide amazing syntactic power in Python.

Ruby, from the examples given, seems quite similar, except that the "syntactic sugar" is somewhat inverted. I don't think it makes for a huge difference, but I don't have any Ruby experience.


Or, in Python 3.3 and above:

  class Stuff:
    def __init__(self):
        self.a_list = [1,2,3,4]
    def __iter__(self):
        yield from self.a_list


Can't you just:

    def __iter__(self):
        return iter(self.a_list)


I was not trying to go for the shortest code, but to show idiomatic code where you could easily do something else instead of just returning same values (otherwise, there is no value in wrapping a list with a class at all).

With both `iter(self.a_list)` or `yield from` you'd have to add a list comprehension in there to process each element, and with no Ruby-like blocks in Python, that limits what one can do.

But they are both definitely good approaches to highlight!


You mean generator, not list comprehension. Either way, you wouldn't `yield from` a list comprehension, you'd just use a loop.

E.g. why write:

    yield from (a*2 for a in self.a_list)
When you can:

    for a in self.a_list:
        yield a*2


Exactly, that's the reason I used a loop in the original snippet :)


Author here, I think this would have made the post better for sure, especially to contrast how `yield` does the opposite thing in each language


The article has some misunderstandings about idiomatic ruby imo.

> Ruby keeps going with its methods-first approach, except instead of each we have a new set of methods commonly implemented on collections, as below:

And then you show map, select, etc being re-implemented.

But once you have "each" defined, you'd just include "Enumerable" and get all the others for free.

As a separate point, I almost never implement each on a custom object in ruby. There are probably "library code" cases where it's appropriate, but in day to day work it should be rare. Typically I'd put objects in an ordinary array instead.

Nit: Ruby style guide (and most experienced code I've seen) never uses parens to invoke a method with no args.


    As a separate point, I almost never implement 
    each on a custom object in ruby. There are 
    probably "library code" cases where it's appropriate
Likewise. I've worked with Ruby fulltime since 2014 and never done it in actual project code, only in book exercises, and I'm not sure I've seen it in any gems I've dove into, though I've never poked around in ActiveRecord.

Like you said, I can't think of too many reasons why one would want to implement #each -- on a daily basis I'm just putting various objects into arrays, hashes, etc.

I tend to write very "boring" Ruby. Ruby gives you lots of ways to get wacky, but IMO the default best practice would be to keep it simple and avoid the cute stuff unless you really have a reason.


I didn't think the author was saying you should always implement a .each method. I thought the implementations were for demonstration purposes, that .each is a method like any other that can be overridden.


that's how i read it too, for the purpose of the technical demonstration


There are other details that are wrong. Ruby does have iterators — except they're called enumerators. The standard implementation is called Enumerator:

    >> [1, 2, 3].each
    => #<Enumerator: [1, 2, 3]:each>
    >> e = [1, 2, 3].each
    => #<Enumerator: [1, 2, 3]:each>
    >> e.next
    => 1
    >> e.next
    => 2
    >> e.next
    => 3
    >> e.next
    Traceback (most recent call last):
            2: from (irb):17
            1: from (irb):17:in `next'
    StopIteration (iteration reached an end)
Enumerators behave similarly to generators in Python thanks to utilities like #to_enum:

    class X
      def each
        yield 1
        yield 2
        yield 3
      end
    end

    >> x = X.new.to_enum
    >> x.each
    #<Enumerator: #<X:0x00007fd30c93dfa0>:each>
    >> x.next
    1
    [etc.]
Ruby's for loops actually use enumerators, just like Python. It's just not used as much, for cultural reasons; most devs these days favour Ruby's data-oriented inversion of control.


Python and Ruby are quite different when writing code, but have almost the exact same technical abilities and limitations in the grand scheme of things. I've always felt there's little reason to learn both.


Ruby is way more capable and expressive than Python. There is so much more you can do with Ruby metaprogramming and don't get me started on Python's feeble lambdas which bear the mark of a BDL imposing his distaste for functional programming. Python is the VHS of programming - widely adopted but technically inferior. Ruby appeals to devs who value elegance of design. Take Sonic Pi (https://sonic-pi.net), for example - I can't imagine sam Aaron producing anything like this in Python. The DSL is everything in this app as with Rails.


Not sure how they compare feature-wise, but take a look at FoxDot:

https://github.com/Qirky/FoxDot


After experiencing Sam Aaron's Overtone - a Clojure REPL for SuperCollider - this does not exactly inspire:

    d1 >> play(P["x( x)  "].palindrome().zip("---[--]").zip(P["  o "].amen()))


Yes exactly, you're not really going to learn anything (syntax aside) by picking up the second, unless you needed it for work reasons or whatever there's surely several other paradigmatically different languages that will be more interesting/instructive to learn.


I'd add elixir to that list too. For the most part, the language differences are not really capitalised on.


> but definitely doesn't involve defining a custom `each` method on your class...

Author here! Thanks for the feedback.

I suspected as much, and was more or less writing it to be illustrative. Though I agree I am bad at Ruby :)


I don't think jrochkind is correct at all, if you want to make a library that has a datastructure that you want to implement custom iteration on, then it is most definitely idiomatic ruby to implement `each` for it. Your article was spot on in my opinion.

That said, in the almost 15 years I've been doing Ruby as my preferred programming language, I think I can count the amount of times I implemented a custom `each` method on one, maybe two hands.

As an example of how `each` is idiomatic, consider the Enumerable mixin: https://ruby-doc.org/core-3.0.2/Enumerable.html if you implement `each` on your class that mixin gives you all those methods (such as select) for free.


Yeah.. totally.. 10 years, I've never seen a custom 'each'


> syntactic support in the language for cleanly passing a single in-line defined lambda/closure object as an argument

I think Perl has this too? Or, well, I should say that Perl allows you to do anything, so even if Perl doesn't let you do it, you can still do it in Perl.


It's the norm in Scala too.

And it's a bit overdone in (older) JS (libraries).


I don't know scala, but the notable thing about ruby vs JS is how ruby provides _syntactic_ support for passing an inline-defined function.

In JS:

    someObj.someMethod(function(something) {
       something
    });
Compare to ruby with the block arg:

    someObj.someMethod do |something|
      something
    end
That `do` is a special syntactic thing avaialble for passing a single inline-defined closure arg. (You can pass one held in a reference instead of inline-defined, but it actually takes an extra somewhat obtuse step -- the syntax is optomized for inline-defined).

This "affordance" says "Yes, we make it really easy to do this, the stdlib does it a lot, please consider doing it all the time in your own code", which goes along with some of what the OP is discussing. Why we write `collection.each {|something| something}` (the braces are an alternate way to pass a block) instead of `for something in collection do...`


I think the crucial difference is that a block can control the flow of the enclosing frame, somewhat analogously to python context managers. For example

  def get_widget
    with_lock do
      return @widget # returns from the method call
    end
  end

  def update_interesting_gadget
    @gadgets.each do |g|
      g.with_lock do
        if g.is_theone?
          g.update
          break # breaks the enclosing each's while loop
        end
      end
    end


So Anonymous Ruby blocks act like (or are) Procs (which return from the parent method), not like lambdas, would you say that?


In modern JS wouldn't you use an arrow function?

  someObj.someMethod(something => something);


The main advantages of Ruby blocks over that approach are:

- they have special control flow that interacts with the method call or surrounding function. ie. calling `break` in `something` can early return from `someMethod`, or calling `return` will return from the function containing the `someMethod` call (blocks use `next` to return from them)

- due to using separate syntax / being a separate language construct, there is far better ergonomics in the presence of vargs or default values

Take this contrived example for instance:

  def some_method(a = 42)
      b = yield
      puts "Hey #{a} #{b}"
  end

  some_method do
    break
  end

In JS you would have to something horrible like this:

  const breakSomeMethod = {}; // Could alternatively use an exception

  function someMethod(one, two) {
    var f, a;
    if (typeof one == 'function') {
      f = one;
      a = 42;
    } else {
      a = one;
      f = two;
    }

    var b = f();
    if (b === breakSomeMethod) {
      return;
    }

    console.log(`Hey ${a} ${b}`)
  }

  someMethod(() => breakSomeMethod);


In Scala it's called blocks [0], and if a function/method expects one you can provide it inline, eg:

    myList.foreach { x => doTheThingToTheThingEvery(x, 100 millis) }

Of course it's sometimes a bit too much. It starts out cute [1] and handy [2][3][4], then [5] ... [6] :)

[0] https://docs.scala-lang.org/tour/basics.html#blocks

[1] https://scastie.scala-lang.org/tpfgE80WTc6QQaHslraJag

[2] https://www.playframework.com/documentation/2.8.x/ScalaActio...

[3] https://stackoverflow.com/q/50370202/44166

[5] https://www.playframework.com/documentation/2.8.x/ScalaActio...

[6] https://miro.medium.com/max/784/1*EMiSTuRIxUPCzWOgLkiXoA.jpe...


Scala doesn't really have a name for this, it simply permits you to provide a block in lieu of a parenthesized argument list. Given that blocks are expressions:

    val a = { val x = 2; x + 1 }
the syntax you are describing is just a function-valued block.


as a rubyistpythonista citizen, i agree




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: