[HN Gopher] Ruby Blocks
       ___________________________________________________________________
        
       Ruby Blocks
        
       Author : stonecharioteer
       Score  : 168 points
       Date   : 2025-10-14 15:58 UTC (4 days ago)
        
 (HTM) web link (tech.stonecharioteer.com)
 (TXT) w3m dump (tech.stonecharioteer.com)
        
       | stonecharioteer wrote:
       | I've been very taken by Ruby and how it uses blocks everywhere!
       | This is an article I wrote just to emphasize that.
        
         | pjmlp wrote:
         | Have a look at Smalltalk blocks, or FP languages, to see where
         | Ruby's inspiration comes from.
        
           | frou_dh wrote:
           | An interesting thing about Smalltalk and Ruby blocks is that
           | they aren't just anonymous functions/lambdas, right? i.e. if
           | you 'return' / '^' in a block, it's the context around the
           | block that you return from, not just the block itself? That's
           | what struck me about both of them when I was used to thinking
           | in basic Lisp terms.
        
             | pjmlp wrote:
             | Yes, that is why that behaviour is known as closures.
             | 
             | Also why in languages like C++, you get to control what is
             | captured from the calling context.
        
             | vidarh wrote:
             | As much as I love Ruby (and I love it a lot - I right now
             | have Claude fix eigenclass compilation issues in another
             | window in my long-languishing Ruby compiler hobby project),
             | this is also the root of a lot of confusion.
             | 
             | Ruby has two types of closures: lambda's, where return
             | returns to the caller, and proc's/blocks where return acts
             | as a return in the defining scope.
             | 
             | Beginners often struggle with the distinction between
             | lambda and block, and often will be wildly confused if they
             | ever see "proc" in the wild.
             | 
             | Yet lambda and proc both return a Proc object, and if you
             | take the value of a block, you also get a Proc object...
             | Just with different return semantics...
             | 
             | It'd be nice if this was a bit clearer.
        
           | stonecharioteer wrote:
           | My next post, which is on loops, is about the common stuff
           | with smalltalk as well!
        
           | vidarh wrote:
           | Ruby is basically what happens when Smalltalk and Perl
           | (compare the _command line_ and the  "$"-variables for
           | example) has a baby and it inexplicably looks much better
           | than both parents.
        
         | janfoeh wrote:
         | I discovered Ruby (through Rails) about twenty years ago on the
         | dot. Coming from Perl and PHP it took me a while, but I
         | remember the moment when I had the same realisation you did.
         | 
         | I still love this language to bits, and it was fun to relive
         | that moment vicariously through someone elses eyes. Thanks for
         | writing it up!
        
           | stonecharioteer wrote:
           | I'm glad folks are having fun reading this. I want to write a
           | few more articles, particularly dissecting the internals of
           | Ruby and how amazing it feels.
        
         | PufPufPuf wrote:
         | Take a look at Kotlin, it perfected this idea
        
           | pjmlp wrote:
           | What Kotlin offers is already present in Scala or other
           | languages from ML linage.
        
       | hshdhdhehd wrote:
       | Is a block basically a lambda or is there more to it?
        
         | simonask wrote:
         | They are closures. But Ruby can do interesting and slightly
         | reckless things, like transplanting a closure into a different
         | evaluation scope. It's very powerful, and also very dangerous
         | in the wrong hands.
        
           | hshdhdhehd wrote:
           | Sounds a bit like a lisp macro? Or in JS using eval?
        
         | somewhereoutth wrote:
         | My understanding is that the 'extra thing' is control flow -
         | blocks can force a return in their calling scope. For example a
         | loop that calls a block may be terminated/skipped by a
         | break/continue statement in the block itself. However I'm not a
         | Ruby programmer, so please check my working.
        
         | masklinn wrote:
         | Assuming that by lambda you mean "an anonymous function"[1],
         | for most intents and purposes they are, except their returns
         | are non-local whereas functions usually have local returns.
         | 
         | However blocks are special forms of the language, unless
         | reified to procs they can only be passed as parameter (not
         | returned), and a method can only take one block. They also have
         | some oddities in how they interact with parameters (unless
         | reified to lambda procs).
         | 
         | [1] because Ruby has something called "lambda procs"
        
       | kace91 wrote:
       | Coming from a language with functions as first class objects,
       | blocks felt a bit limited to me, because it feels as if you
       | almost have functions but not really, and they get inputted by a
       | back door. Used for example to:
       | 
       | let isLarge = a => a>100;
       | 
       | numbers.filter(isLarge)
       | 
       | Blocks let you do the same but without extracting the body as
       | cleanly. Maybe it's a chronological issue, where Ruby was born at
       | a time when the above wasn't commonplace?
       | 
       | >When you write 5.times { puts "Hello" }, you don't think "I'm
       | calling the times method and passing it a block." You think "I'm
       | doing something 5 times."
       | 
       | I'm of two minds about this.
       | 
       | On the one hand, I do agree that aesthetically Ruby looks very
       | clean and pleasing. On the other, I always feel like the mental
       | model I have about a language is usually "dirtied" to improve
       | syntax.
       | 
       | The value 5 having a method, and that method being an iterator
       | for its value, is kinda weird in any design sense and doesn't
       | seem to fix any architectural order you might expect, it's just
       | there because the "hack" results in pretty text when used.
       | 
       | These magical tricks are everywhere in the language with
       | missing_method and the like, and I guess there's a divide between
       | programmers' minds when some go "oh that's nice" and don't care
       | how the magic is done, and others are naturally irked by the
       | "clever twists".
        
         | WJW wrote:
         | > The value 5 having a method, and that method being an
         | iterator for its value, is kinda weird in any design sense and
         | doesn't seem to fix any architectural order you might expect,
         | it's just there because the "hack" results in pretty text when
         | used.
         | 
         | I don't think this is particularly weird, in Ruby at least. The
         | language follows object orientation to its natural conclusion,
         | which is that _everything is an object, always_. There is no
         | such thing as  "just data" in Ruby, because everything is an
         | object. Even things that would just be an `int` in most other
         | languages are actually objects, and so they have methods. The
         | `times` method exists on the Integer classes because doing
         | something exactly an integer number of times happens a lot in
         | practice.
        
           | kace91 wrote:
           | I don't have an issue with the "everything's an object" part,
           | because it _is_ consistent, even though it gets a bit trippy
           | when classes are objects as well and they are implementation
           | of a Class class which is an implementation of itself
           | (trickery again!).
           | 
           | The issue is more with this part:
           | 
           | >The `times` method exists on the Integer classes because
           | doing something exactly an integer number of times happens a
           | lot in practice.
           | 
           | It is practical, but it breaks the conceptual model in that
           | it is a hard sell that "times" is a property over the "5"
           | object.
           | 
           | The result is cleaner syntax, I know, but there is something
           | in these choices that still feels continually "hacky/irky" to
           | me.
        
             | WJW wrote:
             | Perhaps I've been doing Ruby for too long, but it's still
             | not that weird to me. The quantity "5" is very abstract
             | without anything to have "5" of. That is why "5.days" and
             | "5.times" exist, among others. Mathematically it makes just
             | as much sense to start with the amount and add the unit
             | later than it does to start with the unit and add the
             | amount later (ie like `time_to_wait = SECONDS_IN_A_DAY * 5`
             | as you might do in some other languages).
        
               | kace91 wrote:
               | Maybe it is clearer if I explain it in syntactic terms?
               | In my mental model objects are nouns (described entities)
               | and methods are verbs - actions over the noun.
               | 
               | process.start() is the action of starting done by the the
               | noun that is the process.
               | 
               | It's not exactly a matter of naming, as some methods are
               | not explicitly verbs, but there is almost always an
               | implicit action there: word.to_string() clearly has the
               | convert/transform implication, even if ommitted for
               | brevity.
               | 
               | I see no path where 5 is a noun and times the verb, nor
               | any verb I can put there that makes it make sense. If you
               | try to stick a verb (iterate?) it becomes clear that 5 is
               | not the noun, the thing performing the iteration, but a
               | complement - X iterates (5 times). Perhaps the block
               | itself having a times object with 5 as an input would
               | make it more standard to me (?).
               | 
               | But I do understand that if something is extremely
               | practical a purist/conceptual argument doesn't go very
               | far.
        
               | oezi wrote:
               | I have been doing Ruby for so long that it feels very
               | natural to apply a method in this way on the instance.
               | 
               | false.not
               | 
               | applies the not method on the false instance in the same
               | way that
               | 
               | car.start
               | 
               | in every OO language calls the start method on car as the
               | receiver.
               | 
               | So filter(list) feels just wrong when you are clearly
               | filtering the list itself.
        
               | nasmorn wrote:
               | Although I prefer Elixir currently I agree that ruby at
               | least goes all the way in on OO and not having to
               | remember which feature is implemented as a language
               | syntax and what is just a method invocation is a strength
               | not a weakness. It is different in other languages for
               | historical performance reasons really.
        
               | kace91 wrote:
               | list.filter is ok! Filtering is an action that applies to
               | a list
               | 
               | false.not is borderline but if read as false.negate it
               | makes sense (negating is an action that applies to a
               | Boolean value). That wording screws the chaining though.
               | 
               | 5.times is where the pattern breaks: times is not an
               | action that applies to a number (nor an action at all).
               | It's the block the one that should repeat/iterate - but
               | Ruby breaks the rule there and blocks are not an object
               | (!). If they were you could block.repeat(5) which IMO is
               | cleaner.
        
               | chao- wrote:
               | There is a bit of personal preference in what "applies to
               | a number", but I see what you mean.
               | 
               | As a slight correction, a block is indeed an object! They
               | are received by methods as an instance of the Proc class:
               | def inspects_block(&block)         puts block
               | puts block.class       end       inspects_block { "foo" }
               | # => #<Proc:0x0000000000000000>       # => Proc
               | 
               | You can even add a 'repeat' method to these in the way
               | that you specified, although you will need to add '->' to
               | declare the block (as a lambda, which is also just an
               | instance of Proc) before you call #repeat on it:
               | class Proc         def repeat(n)           n.times {
               | self.call }         end       end       ->{ puts("foo")
               | }.repeat(3)       # => foo       # => foo       # => foo
        
               | kaiuhl wrote:
               | Blocks are actually instances of the Proc class. There
               | are helper methods to handle blocks passed to methods in
               | a lightweight manner but you can also accept the block as
               | a method argument, e.g.,                 class Integer
               | def times(&blk)           i = 0           while i < self
               | blk.call(i)             i += 1           end         end
               | end
        
               | kevinmchugh wrote:
               | Fun fact - there's no Boolean class in Ruby. True is an
               | instance of TrueClass and false is an instance of
               | FalseClass
        
               | isr wrote:
               | Hmm, but that's not really a "breakage" in ruby, it's
               | more an aesthetic argument over which objects should have
               | which logic. It's like naming things. Smalltalk (& ruby)
               | has 'select'. Everyone else uses 'filter'.
               | 
               | To some, 5.times seems very readable & logical. It's like
               | arguing over the "right" colour scheme to use while
               | coding (BTW, the correct answer is solarised light, but
               | with black foreground text!!)
        
               | bigtunacan wrote:
               | It's not just about practicality. Ruby is using message
               | passing, not method calling. This is fundamentally
               | different and a bit foreign to the larger community. Then
               | ruby layers syntactic sugar on top that hides this.
               | 
               | Behind the scenes everything is a message passed using
               | __send__ and you can do this directly as well, but you
               | generally don't.
               | 
               | So when you write
               | 
               | 5.times { puts "Hello" }
               | 
               | It's sort of expected by the average programmer that you
               | are telling 5 to call the times method and expect it to
               | exist and do what it's told.
               | 
               | In reality you have indirectly sent a message that looks
               | like
               | 
               | 5.__send__(:times) { puts "Hello" }
               | 
               | What we are really doing is sending a message to 5 (the
               | receiver) and giving it the opportunity to decide how to
               | respond. This is where method_missing comes in to allow
               | responding in a custom fashion regardless if a method was
               | explicitly defined.
               | 
               | So you're not telling 5 to call the method times, rather
               | you are asking, "Hey 5, do you know how to handle the
               | message times?"
               | 
               | These are fundamentally different things. This is
               | actually super important and honestly hard to really grok
               | _especially_ in ruby because of the syntactic sugar. I
               | came from a C/C++ background originally, then Java and
               | then moved to Ruby. After a few years I thought I
               | understood this difference, but honestly it wasn't until
               | I spent a couple years using Objective-C where message
               | passing is happening much more explicitly that I was able
               | to truly understand the difference in a way that it
               | became intuitive.
        
               | anamexis wrote:
               | I'm a rubyist, but _how_ is message passing fundamentally
               | different from method calling? I get that method_missing
               | adds a twist, but your comment doesn't explain what the
               | fundamental difference _is_.
               | 
               | Especially in the context of Fixnum#times. How does
               | message passing vs method calling matter there? #times is
               | just a method on Fixnum.
        
               | isr wrote:
               | Because it leaves it up to the object being called what
               | to actually do with the message. The object you're
               | talking to might be forwarding its messages to another
               | object in another ruby instance on another machine (if
               | the current machine is getting fully loaded, etc), and
               | the caller would be none the wiser. And crucially, the
               | caller wouldn't have to be modified to enable this. The
               | logic for this would be entirely within the object being
               | called.
               | 
               | So the difference isn't just with method_missing.
               | 
               | With "method calling" as you put it, the program blows up
               | if that object doesn't have that method, WHEN YOU CALL
               | IT.
               | 
               | Basically, this smalltalk oo paradigm is about shifting
               | where you put more of your logic. At & around the "call"
               | site, or within the object whom you're calling &
               | entrusting to do something useful with it.
               | 
               | All hearkening back to Alan Kay's original ideas about
               | biology influencing how we organise code, and having a
               | program be 1000's of "little black boxes" where work gets
               | done by all these boxes talking to each other.
               | 
               | Which is why smalltalk (& ruby implements the Smalltalk
               | object model to its entirety) actually has an awful lot
               | in common with Erlang & other BEAM runtimes, even though
               | those are functional languages. Because once you get
               | beyond the techy buzzwords, the main ethos behind them is
               | actually quite similar.
        
               | anamexis wrote:
               | I guess what I'm getting at, is that I don't understand
               | how difference actually informs anything concretely, as
               | in the example of Fixnum#times, where this discussion
               | started. Why is it super important to understand this
               | fundamental difference?
        
             | gray_-_wolf wrote:
             | > that "times" is a _property_ over the "5" object
             | 
             | Maybe here is the confusion, ruby is based on message
             | passing, so the `times` is a message you are sending to 5,
             | not a property of it.
        
               | bigtunacan wrote:
               | I think you're right, but I also suspect that doesn't
               | clear up anything for most people as in my experience
               | they generally don't grok the difference unless they've
               | already spent a significant amount of time in something
               | like smalltalk or Objective-C
        
             | majormajor wrote:
             | When you're new it just looks like a weird-but-reasonable
             | syntax.
             | 
             | When you learn the language you really fall into two camps:
             | 
             | - ah, yes, this is unusual, but it's consistent and now i
             | understand the language
             | 
             | - this is way too clever
             | 
             | I'm more in the first camp.
        
             | cortesoft wrote:
             | Times isn't a property of the 5 object, it is a method of
             | the 5 object.
        
           | TOGoS wrote:
           | `5.times` is not so outlandish, though it would seem better
           | for that to be in a `Loop` library or something
           | (`Loop.times(5) { do some stuff }`).
           | 
           | The `5.days` example that was posted somewhere else in this
           | thread might be a better example. It is not, as far as I can
           | tell, part of Ruby's core library, thank goodness, but it is
           | the kind of thing the methods-as-hacks-for-syntax culture
           | seems to encourage. My question being "why the heck should
           | the number 5 know anything about days? What does 5.days even
           | mean, given that there are various ways to interpret 'a
           | day'?"
           | 
           | This kind of bad design has made its way all over the place.
           | The Java mocking libraries that my coworkers like to use are
           | full of it. Long chains of method calls that appear like
           | they're trying to 'look like English' but make no damn sense,
           | so you have to dig into each method call and what kind of
           | thing it returns to understand what this chain actually
           | means.
        
           | layer8 wrote:
           | With similar justification, you could argue that blocks
           | should have a method `times` taking an integer, because
           | repeating a block an integer number of times happens a lot in
           | practice. I'd even argue that it is conceptually closer to
           | blocks than to integers, and therefore the method belongs on
           | the block. Hence you'd write `{ puts "Hello" }.times 5`.
           | 
           | But blocks are not objects in Ruby, so you can't do that, and
           | everything actually _isn't_ an object in Ruby.
           | 
           | Also, it's even more common to do something depending on
           | whether a condition is true or false, but true and false in
           | Ruby don't have a method to (not) execute a block on them,
           | and you use a non-OOP `if` instead, so what's up with that?
        
           | bashkiddie wrote:
           | > everything is an object, always.
           | 
           | I beg to differ. What object does the method `puts` belong
           | to? Why do you not call puts with its objects name?
           | 
           | Ruby has a concept of mixins (Golang interfaces), these are
           | not objects. Neither is `puts`
        
             | kazinator wrote:
             | Answerable by a few minutes of googling. Sort of:
             | 
             | puts is a method which has a class: the Method class:
             | irb(main):001:0> method(:puts)       => #<Method:
             | main.puts>       irb(main):002:0> method(:puts).class
             | => Method
             | 
             | Everything being a confused muddle in Ruby, there is
             | evidently some Kernel base class that is injected into
             | every Object, and puts is a private method in that:
             | irb(main):003:0> 3.puts()       Traceback (most recent call
             | last):               2: from /usr/bin/irb:11:in `<main>'
             | 1: from (irb):3       NoMethodError (private method `puts'
             | called for 3:Integer)
             | 
             | The Method class of puts is a real class with methods and
             | all:                 irb(main):004:0>
             | method(:puts).class.methods       => [:allocate,
             | :superclass, :<=>, :<=, :>=, :==, :===, :autoload?,
             | :autoload, :included_modules, :include?, :name, :ancestors,
             | :attr, :attr_reader, :attr_writer, :attr_accessor,
             | :instance_methods, :public_instance_methods,
             | :protected_instance_methods, :private_instance_methods,
             | :constants, :const_get, :const_set, :const_defined?,
             | :class_variables, :remove_class_variable,
             | :class_variable_get, :class_variable_set,
             | :class_variable_defined?, :public_constant, :freeze,
             | :inspect, :deprecate_constant, :private_constant,
             | :const_missing, :singleton_class?, :prepend, :class_exec,
             | :module_eval, :class_eval, :include, :<, :>,
             | :remove_method, :undef_method, :alias_method,
             | :protected_method_defined?, :module_exec, :method_defined?,
             | :public_method_defined?, :to_s, :public_class_method,
             | :public_instance_method, :define_method,
             | :private_method_defined?, :private_class_method,
             | :instance_method, :instance_variable_set,
             | :instance_variable_defined?, :remove_instance_variable,
             | :instance_of?, :kind_of?, :is_a?, :tap,
             | :instance_variable_get, :instance_variables, :method,
             | :public_method, :singleton_method,
             | :define_singleton_method, :public_send, :extend, :to_enum,
             | :enum_for, :pp, :=~, :!~, :eql?, :respond_to?, :object_id,
             | :send, :display, :nil?, :hash, :class, :singleton_class,
             | :clone, :dup, :itself, :yield_self, :taint, :tainted?,
             | :untrust, :untaint, :trust, :untrusted?, :methods,
             | :frozen?, :protected_methods, :singleton_methods,
             | :public_methods, :private_methods, :!, :equal?,
             | :instance_eval, :instance_exec, :!=, :__send__, :__id__]
        
               | cortesoft wrote:
               | > Everything being a confused muddle in Ruby
               | 
               | It really isn't a confused muddle, the rules are very
               | clear. Just because it doesn't match what you expect from
               | your other language experience doesn't mean it isn't
               | clear.
        
               | pxc wrote:
               | I'm not a Rubyist really, but I started using it at work
               | a few weeks ago for a very small script. I'm not allergic
               | to RTFM, so I picked up the canonical reference book on
               | Ruby and occasionally visit the official docs for the
               | language. I agree: the structure is clear and
               | straightforward and there's nothing difficult about
               | learning it.
               | 
               | It's also the opposite of magic; magic is when language
               | features can't be described in terms of the language
               | itself.
        
               | WJW wrote:
               | It's not as much a mystery as you think, the Kernel
               | module is pretty well documented and states right in the
               | first paragraph that its methods are available in all
               | Ruby objects: https://ruby-doc.org/core-3.0.2/Kernel.html
        
             | chao- wrote:
             | _> Ruby has a concept of mixins (Golang interfaces), these
             | are not objects._
             | 
             | Ruby "mixins" are the affordance of sharing instance
             | methods from a module via a keyword. Modules are objects,
             | and are instances of class Module:                 module
             | ProvidesFooMixin         def foo = "foo"       end
             | class Bar         include ProvidesFooMixin       end
             | Bar.new.foo       # => "foo"
             | puts(ProvidesFooMixin.class)       # => Module
             | ProvidesFooMixin.object_id       # => (some integer value)
             | 
             | _> Neither is `puts`_
             | 
             | Like all methods, `puts` is an object:
             | method(:puts)       # => #<Method: Object(Kernel)#puts(*)>
             | method(:puts).class       # => Method
             | method(:puts).object_id       # => (some integer value)
             | 
             | Here you see evidence of where `puts` comes from:
             | Kernel#puts via Object, which I will now explain in detail.
             | 
             |  _> What object does the method `puts` belong to?_
             | 
             | It belongs to the object you are calling it from within.
             | You don't need to call `puts` with a receiver because it is
             | an instance method, just like you don't need to call an
             | instance method `foo` via `self.foo`. But you _could
             | choose_ to use a receiver, since the `puts` you know and
             | love is just another instance method. You can try
             | `self.puts` for yourself in some context!
             | 
             | Your classes (and their instances) inherit this `self.puts`
             | instance method from the Object class, which includes the
             | Kernel module, which provides `Kernel#puts`. So the only
             | reason you can send it as a message without a receiver is
             | because it is just another instance method (again, the same
             | as calling instance method `#foo` without using
             | `self.foo`).
             | 
             | Caveat: You can build an "alternate universe" object
             | hierarchy by inheriting from BasicObject, and in your
             | alternate universe, you can choose to not `include Kernel`,
             | and you will see that instances of your new objects do not
             | have access to `puts` in their instance scope.
        
             | jfabre wrote:
             | Modules (mixins) are objects, classes are also objects (of
             | type Class).
             | 
             | The Kernel module is included in the Object class, which
             | means its methods are available to every Ruby object and
             | can be accessed from virtually any scope, including the
             | top-level (global) context where self is an instance of
             | Object called "main."
        
             | cortesoft wrote:
             | Well, "puts" is a method defined in the Kernel module,
             | which is included in the Object class, which means it is
             | available in all contexts (since every object is a subclass
             | of the 'Object' class). So, to answer your question, the
             | puts method belongs to the Kernel module object, and the
             | Kernel module object is an instance of a Module object.
        
             | WJW wrote:
             | `puts` is just a method of the Kernel module: https://ruby-
             | doc.org/3.4.1/Kernel.html#method-i-puts, just like `p` and
             | many others. Kernel is included in the Object class that is
             | the root of the class hierarchy, so its methods are
             | available in every ruby object.
             | 
             | Mixins are just modules, which are objects, which you can
             | call methods on. (Or rather, send messages to) You can
             | easily verify this in irb calling a method on (for example)
             | the Enumerable module:                   irb(main):001>
             | Enumerable.class         => Module
             | 
             | You are right that a module is not a class, and it is not
             | possible to call `.new` on it. But the module itself is
             | very much an object.
        
             | pxc wrote:
             | > I beg to differ. [...] Why do you not call puts with its
             | objects name?
             | 
             | In the first place, I'd say what you're asking for goes
             | beyond "everything is an object".
             | 
             | But I think your questions can be answered in a way that
             | affirms that "everything is an object" in Ruby anyway.
             | 
             | > Why do you not call puts with its objects name?
             | 
             | Because it belongs to whatever object you're working in
             | already; `puts` is identical to `self.puts`. And yes,
             | you're always working in an object:
             | https://bparanj.gitbooks.io/ruby-
             | basics/content/chapter1.htm...
             | 
             | > What object does the method `puts` belong to?
             | 
             | As indicated above, it belongs to the object `self`. It
             | gets added object via the mixing-in Kernel module into
             | Object. Kernel is itself an instance of class Module:
             | https://docs.ruby-lang.org/en/3.4/Module.html
             | 
             | The `puts` in Kernel delegates to `puts` from IO, which is
             | likewise an instance method belonging to the object you can
             | refer to by the name `$stdout`: https://docs.ruby-
             | lang.org/en/3.4/IO.html
             | 
             | > Ruby has a concept of mixins [and], these are not
             | objects.
             | 
             | Sure they are. Mixins themselves inherit from Module, and
             | Modules are also objects (just like classes are).
             | 
             | Some highlights from Chapter 27 ("Library Reference: The
             | Class Model") of the recent edition of the pickaxe book
             | (emphasis mine):
             | 
             | > The Kernel module is included by class Object, so its
             | methods are available in every Ruby object. One of the
             | reasons for the Kernel module is to allow methods like
             | `puts` and `gets` to be available everywhere and even to
             | _look like_ global commands. Kernel methods allow Ruby to
             | _still maintain an "everything is an object semantics"._
             | 
             | and regarding mixins:
             | 
             | > The Module class is the class of any module you declare
             | with the `module` keyword. Each module is an instance of
             | class Module.
             | 
             | on Object:
             | 
             | > Object is the parent class of (almost) all classes in
             | Ruby unless a class explicitly inherits from. BasicObject.
             | [...] Object _mixes in_ the _Kernel module_ , making the
             | built-in functions globally accessible.
             | 
             | tl;dr: mixins in Ruby are instances of class Module, and
             | their methods end up bound to instances of class Object.
             | Abstract module methods that don't belong to a concrete
             | instance of some class that mixed in their module belong to
             | the Object that is the module itself (the instance of class
             | Module). (The same kind of thing is how class methods
             | work.)
        
         | judofyr wrote:
         | Blocks are fundamentally different from functions due to the
         | control flow: `return` inside a block will return the _outer_
         | method, not the block. `break` stops the whole method that was
         | invoked.
         | 
         | This adds some complexity in the language, but it means that
         | it's far more expressive. In Ruby you can with _nothing but
         | Array#each_ write idiomatic code which reads very similar to
         | other traditional languages with loops and statements.
        
           | oezi wrote:
           | You are right on return (use next in a block), but break uses
           | block scope.
        
             | judofyr wrote:
             | Maybe I explained it a bit imprecise. I was trying to
             | explain the following behavior:                   def foo
             | p 1           yield           p 2         end
             | foo { break }
             | 
             | This only prints "1" because the break stops the execution
             | of the invoked method (foo).
        
               | Mystery-Machine wrote:
               | WAT? I'm a 12+ years Ruby developer and I didn't know
               | this.
        
           | vidarh wrote:
           | More specifically blocks (and "proc"'s) return _from the
           | defining scope_. This is just a minor clarification, but it
           | matters, because if you pass a block _down_ from where it is
           | defined, and the block calls  "return" it will still not just
           | exit from the method where it was called, but from the method
           | where it was _defined_.
           | 
           | This can sometimes be useful: A calling method can pass down
           | a block or proc to control if/when it wants an early return.
           | 
           | Basically Ruby has two types of closures:
           | 
           | * A return in a lambda returns _to the calling scope_. So
           | basically, it returns to after where the  "call" method is
           | invoked.
           | 
           | * A return in a block or a proc returns from the _scope in
           | which it was defined_ (this is also why you get
           | LocalJumpError if you try to return a block or a proc, but
           | not a lambda to the method _calling_ the one where the block
           | or proc is defined).
           | 
           | When you name a block, you get a Proc object, same as you get
           | when you take the value of a lambda or proc.
           | 
           | In practice, that blocks in MRI are not Proc objects already
           | is just an implementation detail/optimisation. I have a long-
           | standing hobby project to write a Ruby compiler, and there a
           | "proc" and a bare block are implemented identically in the
           | backend.
        
         | jhbadger wrote:
         | If you are familiar with a true object-oriented language like
         | Smalltalk (rather than the watered-down form of OO in C++,
         | Java, etc.), an integer like 5 having methods makes sense
         | because it (like everything else) is an object. Objects in Ruby
         | aren't just window dressing -- they are its core.
        
           | ck45 wrote:
           | But then Ruby only goes half way, not unlike the "watered-
           | down form" in your term. Why is `#times` a method of Integer,
           | but `#if` (or `#ifTrue`) not a method of booleans like in
           | Smalltalk? Ruby does the same cherry picking from Smalltalk
           | like everybody else, just different cherries. When looking at
           | Ruby, it feels like the simple examples are all nice and
           | clean but then the weird details start to appear and the
           | language feels more hacky than others (like Ned Flander's
           | house in Simpsons S08E08).
        
             | chao- wrote:
             | #if and #ifTrue are yours if you want them:
             | class TrueClass         def if = true         def ifTrue =
             | true       end            class FalseClass         def if =
             | false         def ifTrue = false       end
             | true.if       # => true       false.if       # => false
        
               | wild_egg wrote:
               | In Smalltalk those methods don't return `true`. They take
               | a block and evaluate it if the boolean receiving the
               | message                   (a > b) ifTrue: [ "do
               | something" ]
               | 
               | EDIT: to clarify what's happening there, `>` is a message
               | sent to `a` that will result in a boolean. The True class
               | and False class both understand the ifTrue: message and
               | `True>>ifTrue:` executes the block whereas
               | `False>>ifTrue:` just throws it away.
               | 
               | There's no `if` keyword in the language. Control flow is
               | done purely through polymorphism.
        
               | chao- wrote:
               | I apologize for my lack of Smalltalk knowledge. As you
               | can imagine, you can do similar in Ruby by defining
               | ifTrue to accept a block, even adding ifTrue on other all
               | objects and defining something similar:
               | class TrueClass         def ifTrue(&block) = block.call
               | end            class FalseClass         def
               | ifTrue(&block) = nil       end            class Object
               | def ifTrue(&block) = block.call       end
               | class NilClass         def ifTrue(&block) = nil       end
               | 
               | If ck45's core complaint was that this is not baked into
               | the language, I will agree that it is less convenient for
               | lack of a default.
        
               | oezi wrote:
               | Certainly possible: add ifTrue as a method to TrueClass
               | and FalseClass.
               | 
               | It just isn't very fast.
        
               | codesnik wrote:
               | problem is not with ifTrue, and not with it's
               | performance, it's easy to do. it is "ifTrue:ifFalse:"
               | 
               | also it is common to do assignments in the "if", and with
               | actual method and blocks scope of the introduced variable
               | would be different and everyone would be tripping on it
               | all the time.
        
             | codesnik wrote:
             | basically it's because of "else" and "elsif". While ".each"
             | works the same as "for .. in ...; end", it's harder to do
             | "if else" as method which will also return value of the
             | block inside the branch. Smalltalk can do it because
             | "ifTrue:ifFalse:" is _one_ message, ruby didn't go that way
             | syntactically.
        
         | chao- wrote:
         | The "aesthetically pleasing" aspect of blocks is not mutually
         | exclusive with real, first-class functions! Ruby is really more
         | functional than that. Ruby has both lambas and method objects
         | (pulled from instances). For example, you can write:
         | let isLarge = a => a>100;
         | 
         | as a lambda and call via #call or the shorthand syntax .():
         | is_large = ->(a) { a > 100 }       is_large.call(1000)       #
         | => true       is_large.(1000)       # => true
         | 
         | I find the .() syntax a bit odd, so I prefer #call, but that's
         | a personal choice. Either way, it mixes-and-matches nicely with
         | any class that has a #call method, and so it allows nice
         | polymorphic mixtures of lambdas and of objects/instances that
         | have a method named 'call'. Also very useful for injecting
         | behavior (and mocking behavior in tests).
         | 
         | Additionally, you can even take a reference to a method off of
         | an object, and pass them around as though they are a callable
         | lambda/block:                 class Foo         def bar = 'baz'
         | end            foo_instance = Foo.new       callable_bar =
         | foo_instance.method(:bar)       callable_bar.call       # =>
         | 'baz'
         | 
         | This ability to pull a method off is useful because any method
         | which receives block can also take a "method object" and be
         | passed to any block-receiving method via the "block operator"
         | of '&' (example here is passing an object's method to Array#map
         | as a block):                 class UpcaseCertainLetters
         | def initialize(letters_to_upcase)           @letters_to_upcase
         | = letters_to_upcase         end              def format(str)
         | str.chars.map do |char|
         | @letters_to_upcase.include?(char) ? char.upcase : char
         | end.join         end       end            upcase_vowels =
         | UpcaseCertainLetters.new("aeiuo").method(:format)       ['foo',
         | 'bar', 'baz'].map(&upcase_vowels)       # => ['fOO', 'bAr',
         | 'bAz']
         | 
         | This '&' operator is the same as the one that lets you call
         | instance methods by converting a symbol of a method name into a
         | block for an instance method on an object:
         | (0..10).map(&:even?)       # => [true, false, true, false,
         | true, false, true, false, true, false, true]
         | 
         | And doing similar, but with a lambda:
         | is_div_five = ->(num) { num % 5 == 0 }
         | (0..10).map(&is_div_five)       # => [true, false, false,
         | false, false, true, false, false, false, false, true]
        
           | kace91 wrote:
           | That is interesting! I haven't explored Procs much, since I
           | use ruby for a shared codebase at work and I was originally a
           | bit afraid of trying to push unidiomatic ideas in the
           | codebase.
           | 
           | In your experience, is it ok to use Procs for example for
           | extraction of block methods for cleanliness in refactors? or
           | would I hit any major roadblocks if I treated them too much
           | like first-class functions?
           | 
           | Also, is there any particular Rails convention to place
           | collections of useful procs? Or does that go a bit against
           | the general model?
        
             | vinceguidry wrote:
             | You shouldn't have much difficulty, Ruby converts blocks to
             | Procs whenever it needs an actual object. Their semantics
             | are intentionally kept the same. This is unlike lambdas,
             | whose semantics are closer to methods.
             | 
             | Pass the wrong number of of arguments to a Proc or block,
             | it will pass nil for missing args and omit extras. Pass the
             | wrong number of arguments for a method or lambda and you
             | get an ArgumentError. Use the return keyword in a lambda,
             | it returns from the lambda, just like if you call return in
             | a method. In a block or Proc, it returns from the calling
             | method.
             | 
             | So I would feel comfortable leaning on them for refactoring
             | as it's as Ruby intended. Just use lambdas when you want to
             | turn methods into objects and Procs when you want to
             | objectify blocks.
             | 
             | You should get ahold of a copy of Metaprogramming Ruby 2 if
             | you find yourself refactoring a lot of Ruby. It's out of
             | print, but ebooks are available.
        
               | vidarh wrote:
               | Just to clarify here: Both lambdas and procs are Proc
               | objects. Blocks gets turned into Proc objects when you
               | take their value.
               | 
               | So just be clear about whether you're talking about a
               | proc or a Proc...
               | 
               | > In a block or Proc, it returns from the calling method
               | 
               | No, in block or a Proc it returns _from the scope where
               | it was defined_.
               | 
               | Usually this is the same, and so it doesn't usually
               | matter much, but if you pass a proc or a block down to
               | another method, then a return within the block will still
               | return from the method where it was defined.
               | 
               | This can occasionally be useful, as you can use it to
               | pass in a predicate to a method that can control if/when
               | you want to return, irrespective of how deeply nested.
        
               | vidarh wrote:
               | Too late to edit now, and this is what I get for
               | quibbling about casing:
               | 
               | > No, in block or a Proc it returns from the scope where
               | it was defined.
               | 
               | Should of course read:
               | 
               | > No, in a block or a proc it returns from the scope
               | where it was defined.
        
             | Mystery-Machine wrote:
             | This sounds like a really innovative idea. I haven't seen a
             | dedicated place for "collection of useful procs", but one
             | emerging pattern is to use `app/services` and then have a
             | bunch of single-responsibility service classes that each
             | have call or perform method and then you use the service
             | when you need some shared functionality. It's like a proc,
             | but instead it's a class with `#call` method.
        
               | vidarh wrote:
               | > It's like a proc, but instead it's a class with `#call`
               | method.
               | 
               | It's called the "callable" pattern.
        
         | somewhereoutth wrote:
         | Interestingly, in the Lambda Calculus, where everything is a
         | function, a standard representation for a natural number n
         | (i.e. a whole number >= 0), is indeed a function that
         | 'iterates' (strictly, folds/recurses) n times.
         | 
         | E.g. 3:
         | 
         | (f, x) => f(f(f(x)))
        
         | vidarh wrote:
         | This is your example in Ruby:                   isLarge = ->
         | {|a| a > 100 }              numbers.filter(&isLarge)
         | 
         | Or you could replace the first line:                   isLarge
         | = -> { _1 > 100 }
         | 
         | Some people hate that syntax, though. I think for trivial
         | predicates like this, it's fine.
        
       | inopinatus wrote:
       | For this audience it may be worth noting that Ruby's blocks are
       | closures and are passed to methods either anonymously/implicitly
       | or as a named parameter, may be subsequently passed around to any
       | collaborator object, or otherwise deferred/ignored, have the same
       | range of argument arity as methods and lambdas, can even be
       | formed from (and treated similarly to) lambdas, and are thereby
       | fundamental to Ruby's claim to being a multiparadigm language
       | even as they also betray the Smalltalk roots.
       | 
       | In addition they have nonlocal return semantics, somewhat like a
       | simple continuation, making them ideal for inline iteration and
       | folding, which is how most new Rubyists first encounter them, but
       | also occasionally a source of surprise and confusion, most
       | notably if one mistakenly conflates return with result. Ruby does
       | separately have callcc for more precise control over stack
       | unwinding, although it's a little known feature.
        
         | rubyfan wrote:
         | These were some of the best long running sentences I've read in
         | a while. A true rubyist!
        
           | rubybaby99 wrote:
           | > A true rubyist!
           | 
           | When it's all a single paragraph, you'll know they've
           | achieved Rubynirvana.
        
         | dragonwriter wrote:
         | > can even be formed from (and treated similarly to) lambdas
         | 
         | They are also used to create lambdas (even the shorthand
         | stabby-lambda syntax desugars to a call to Kernel#lambda with a
         | block.)
         | 
         | > Ruby does separately have callcc for more precise control
         | over stack unwinding, although it's a little known feature.
         | 
         | callcc is included in CRuby but has been sidelined from Ruby as
         | a language separate from CRuby as an implementation for a
         | while, with Fibers understood to cover the most important use
         | cases for callcc.
        
       | teaearlgraycold wrote:
       | This level of cuteness and obsession with syntax is partly what
       | drives me away from Ruby. A function should just be a function.
       | We don't need to make programming languages look more like
       | English. There are certainly issues with other languages and
       | their verbosity (like with Java). But I don't want to have more
       | ways to do the same thing and worry about how poetic my code
       | reads as English - we should worry how poetic it reads as
       | _computer code_.
       | 
       | That said, we don't need just one programming language. Perhaps
       | Ruby is easier to learn for those new to programming and we
       | should introduce it to students.
        
         | stonecharioteer wrote:
         | I think Ruby teaches a sense of style, but I'm not sure that
         | style carries over to other languages. Python was my primary
         | language for 12 years but I'm disappointed in the depth of
         | Knowledge python Devs have. Most barely understand the language
         | or try to. Ruby seems to coax people into coding into a ruby
         | way. I like that.
        
           | teaearlgraycold wrote:
           | > but I'm disappointed in the depth of Knowledge python Devs
           | have
           | 
           | Not super relevant, but my favorite fun fact about Python
           | (specifically CPython) is that hash(x) where x is an int is
           | always the value of the int - _except_ for -1, because the
           | underlying C function uses -1 to represent errors. So
           | hash(-1) == -2, which means you have a hash collision right
           | around 0 where hash(-1) == hash(-2)
        
       | shevy-java wrote:
       | Blocks yield a lot more flexibility to ruby. It was the primary
       | reason why they are so well-appreciated.
        
       | politelemon wrote:
       | It's only more readable if you already understand it. Otherwise
       | it is not, it requires the same kind of hand waving that happened
       | at the start.
       | 
       | Important to understand that readability doesn't mean it should
       | be closer to natural language, in programming it means that a
       | junior dev troubleshooting that code later down the line can
       | easily understand what's happening.
       | 
       | The python examples are certainly more readable from a
       | maintainability and comprehension standpoint. Verbosity is not a
       | bad thing at all.
        
         | mcphage wrote:
         | > It's only more readable if you already understand it.
         | Otherwise it is not, it requires the same kind of hand waving
         | that happened at the start.
         | 
         | Yes, but it's a core language feature, so if you spend any time
         | programming Ruby, you'll come to understand it.
        
       | varispeed wrote:
       | With autocomplete being so good today, do we really need
       | languages with cryptic syntax that obscure what is going on?
        
         | Alifatisk wrote:
         | To an outsider watching Ruby, it's cryptic, esoteric and maybe
         | magical. But when you actually use it and learn mechanism
         | underneath it, things start to make sense. However, Rubys
         | dynamic architecture have also made it difficult for DX. Things
         | like autocomplete barely works in the ecosystem, because it's
         | so unpredictable until runtime
        
       | Alifatisk wrote:
       | I believe the underlying behaviour of Ruby blocks is one of those
       | mechanics that isn't talked about that much for newcomers, they
       | just get used to how Ruby code look like when they see Rails,
       | Cucumber or RSpec
       | 
       | Blocks and method_missing is one of those things in Ruby is what
       | makes it so powerful! I remember watching a talk where someone
       | was able to run JS snippets on pure Ruby just by recreating the
       | syntax. That proves how powerful Ruby is for creating your own
       | DSL
       | 
       | It's also a double edged sword and something you have to be
       | careful with on collaborative codebases. Always prefer simplicity
       | and readability over exotic and neat tricks, but I understand the
       | difficulty when you have access to such a powerful tool
        
         | nasmorn wrote:
         | IMO blocks are not something to be careful about in ruby. If
         | you don't use blocks you are the weirdo in this language.
         | 
         | Method missing is a different beast altogether. I would
         | probably avoid it nowadays.
        
           | vidarh wrote:
           | There are legitimate uses of method_missing (though often
           | you'd want to call define_method dynamically instead for
           | performance), but they tend to be when you use an object as a
           | proxy for something (say a remote API) where you genuinely
           | can't know the set of messages the object might need to be
           | able to receive.
        
       | dorianmariecom wrote:
       | blocks are just procs :)
        
         | Alifatisk wrote:
         | It does look like it but procs encapsulates a block, but a
         | block alone is not a proc
        
           | kaiuhl wrote:
           | That's just a performance optimization because blocks are
           | typically only invoked, not passed or otherwise manipulated.
           | If you want to do anything with the block, you pay a tiny tax
           | and have a Proc instance.
        
       | SafeDusk wrote:
       | I recommend reading Shopify CEO Tobi's try[0] for good example of
       | how Ruby's block behavior and meta-programming makes it easy to
       | create a single file, shell wrapper.
       | 
       | [0]: https://github.com/tobi/try/blob/main/try.rb
        
         | codeduck wrote:
         | what the fuck...
        
       | itsme0000 wrote:
       | Market forces will ensure this system isn't utilized regardless
       | of how good it is. Ruby is simply to similar to Python to
       | consider training people solely in that.
       | 
       | It's a hard reality because I'm sure Ruby is better according to
       | some criteria, but be realistic their share of the market is
       | going to shrink until it's not really an option of most large
       | companies.
       | 
       | I know people will disagree with me, but I wish I knew earlier in
       | my Carr how little this sort of thing matters.
        
         | an0malous wrote:
         | Let's check back in 6 months after the AI bubble pops
        
         | mistercheph wrote:
         | Thankfully, programming languages are subject to strong network
         | effects but not massive ones, Ruby will continue to be a niche
         | language that has active users and projects for many more years
         | than you expect.
        
       | Dan42 wrote:
       | This is really cute and heartwarming.
       | 
       | Back in the day, a lot of people including me reported feeling
       | more comfortable in Ruby after one week than all their other
       | languages with years of experience, as if Ruby just fits your
       | mind like a glove naturally.
       | 
       | I'm glad new people are still having that "Ruby moment"
        
         | nixpulvis wrote:
         | Ruby is still the first tool I reach for if I need to do
         | something quickly and naturally. People are always surprised at
         | how readable the code comes out even compared to python.
        
           | codeduck wrote:
           | The three stages of ruby:
           | 
           | 1. Someone scoffs at you for writing ruby instead of python /
           | go / javascript / kotlin
           | 
           | 2. Then they read the code
           | 
           | 3. Then they install ruby
           | 
           | It's an amazing language and remains my go-to for anything
           | that doesn't need protocol stuff.
        
       | rvitorper wrote:
       | I can't unsee it either. Will try it later
        
       | leemcalilly wrote:
       | Welcome
        
       | thepaulmcbride wrote:
       | Ruby is really let down by the tooling around the language. The
       | language itself would be so much more fun to write if the lsp
       | would reliably jump to the definition of functions etc that seem
       | to appear out of no where. It has been the biggest source of
       | frustration for me while learning Ruby.
        
         | stonecharioteer wrote:
         | I'm hoping the new age tooling that's coming out is going to
         | make things better. I want to contribute to rv personally.
        
         | Mystery-Machine wrote:
         | You're right! Although I get faily far by using Bust-a-gem VS
         | Code extension. (The underlying ripper-tags gem can work with
         | any IDE) https://github.com/gurgeous/bust-a-gem
         | 
         | I have an "on save" hook that runs ripper-tags on every file
         | save. This keeps the definitions always up to date.
        
         | mmcromp wrote:
         | I say the opposite, the lack of tooling highlights the weakness
         | of the language. The drive to make it declarative/mimic
         | "natural" language by reshuffling and overloading can be
         | "delightful" to some, but beyond the paper covered surface is a
         | mess. And for what ? "5.times do something unless" isn't cute
         | to me. It's a dog "talking" by putting peanut butter in his
         | mouth. But I think I'm the only one who feels this way
        
         | Bolwin wrote:
         | And it's twice as bad on windows.
        
       | Mystery-Machine wrote:
       | Ruby is beautiful.
       | 
       | It's weird, and different and therefore a bit repulsive (at least
       | to me it was) at first. But, once you learn it, it's so easy to
       | read it and to understand what's going on.*
       | 
       | * Side-note: Sometimes variables or methods look the same as
       | parenthesis () are optional. So, yes, there's more things that
       | can look like magic or be interpreted in multiple ways, but more
       | times than not, it helps to understand the code faster, because
       | `clients` and `clients()` (variable or method) doesn't matter if
       | all it does is "get clients" and you just need to assume what's
       | stored/returned from that expression. Also "get clients" can be
       | easily memoized in the method implementation so it gets as close
       | as possible to being an actual variable.
        
       | t-writescode wrote:
       | Ruby Blocks and how they're used literally everywhere is one of
       | the hallmarks of why it's so nice to write the first time in that
       | language; and it shaped how I use and select future languages.
       | 
       | Ruby Blocks are almost certainly the reason why I love Kotlin so
       | much - it feels like a well-typed, curly-bracket-styled Ruby in
       | those ways. The collection operation chains in both languages
       | just. feel. good. And I blame Ruby for my first exposure to them,
       | and possibly a lot of people's early exposure to them that helped
       | languages that came after become better.
       | 
       | Ruby does take it to a-whole-nother level though, in particular
       | with its 'space as separator' syntax, so you can make a *robust*
       | DSL that's even more powerful than Kotlin's "if the last param is
       | a function, you can just put a curly bracket and go" style.
        
       | user3113 wrote:
       | Ruby is my favorite language. There is no other language with
       | same ergonomics. I dont understand hype around ts and all
       | frameworks that changes every week.
        
       | username223 wrote:
       | Was this human-generated? Clearly it's self-submitted, and the
       | useless "hero image" looks like lazy AI slop with Pixar
       | characters, but did a human generate the text?
        
       | bashkiddie wrote:
       | I heard some rumor that ruby was a type 2 lisp. There were some
       | guys who rate programming languages on how much lisp features
       | they reassemble. What is the source?
        
       | rockyj wrote:
       | Ruby is still so good to read and hack things with. It is a shame
       | that it is not so popular and you know the reasons why. I still
       | wish with such a good DSL friendly structure, it should have
       | become an IaC de-facto standard language or used in some niche
       | where I could use it freely without question.
        
         | rapind wrote:
         | It was revolutionary for readability and has influenced a lot
         | of newer languages. That's a pretty good legacy IMO.
        
         | culi wrote:
         | The biggest thing holding Ruby back is lack of gradual typing
         | imo. I honestly think javascript is a better fit for IaC. Not
         | only is already the language of the web (everyone has to know
         | _some_ javsacript) but JSDOC is supported by most IDEs giving
         | it gradual typing. Many people don 't realize jsdoc _is_
         | typescript and is a full replacement.
         | 
         | Nowadays I like to reach for Julia for quick one-off scripts
         | and webscraping. It has a beautiful and accessible syntax with
         | built-in gradual typing. I would love to see it more widely
         | adopted in the IaC world
        
       ___________________________________________________________________
       (page generated 2025-10-18 23:01 UTC)