Mind Dump, Tech And Life Blog
written by Ivan Alenko
published under license bootleg
posted at 21. Apr '18

Metaprogramming notes

Singleton methods

The only difference is that we get to use a closure when calling Module#define_method, whereas def is a keyword, which results in a change of scope.

Why can’t it be simpler?

Well, the good news is that you’re programming in Ruby, so feel free to go crazy:

class Object
  def define_method(name, &block)
    singleton = class << self; self; end
    singleton.send(:define_method, name) { |*args| block.call(*args) }
  end
end


str = 'test'
str.define_method(:to_s) { "hello" }
str.define_method(:bark) { "woof!" }
str.define_method(:yell) { "AAAH!" }

puts str.to_s #=> hello
puts str.bark #=> woof!
puts str.yell #=> AAAH!

And, if you’re curious…

You know class methods? Or, in some languages, we’d call them static methods? Well, those don’t really exist in Ruby. In Ruby, class methods are really just methods defined in the Class object’s singleton class.

If that all sounds crazy, take a look at the links I provided above. A lot of Ruby’s power can only be tapped into if you know how to metaprogram - in which case you’ll really want to know about singleton classes/methods, and more generally, the Ruby object model.

HTH

-Charles

Blocks

The top two answers here are wrong. Ruby’s yield is not an iterator like in C# and Python. yield itself is actually a really simple concept once you understand how blocks work in Ruby.

Yes, blocks are a functional programming feature, even though Ruby is not properly a functional language. In fact, Ruby uses the method lambda to create block objects, which is borrowed from Lisp’s syntax for creating anonymous functions — which is what blocks are. From a computer science standpoint, Ruby’s blocks (and Lisp’s lambda functions) are closures. In Ruby, methods usually take only one block. (You can pass more, but it’s awkward.)

The yield keyword in Ruby is just a way of calling a block that’s been given to a method. These two examples are equivalent:

def with_log
  output = yield # We're calling our block here with yield
  puts "Returned value is #{output}"
end

def with_log(&stuff_to_do) # the & tells Ruby to convert into
                           # an object without calling lambda
  output = stuff_to_do.call # We're explicitly calling the block here
  puts "Returned value is #{output}"
end

In the first case, we’re just assuming there’s a block and say to call it. In the other, Ruby wraps the block in an object and passes it as an argument. The first is more efficient and readable, but they’re effectively the same. You’d call either one like this:

with_log do
  a = 5
  other_num = gets.to_i
  @my_var = a + other_num
end

And it would print the value that wound up getting assigned to @my_var. (OK, so that’s a completely stupid function, but I think you get the idea.)

Blocks are used for a lot of things in Ruby. Almost every place you’d use a loop in a language like Java, it’s replaced in Ruby with methods that take blocks. For example,

[1,2,3].each {|value| print value} # prints "123"
[1,2,3].map {|value| 2**value}    # returns [2, 4, 8]
[1,2,3].reject {|value| value % 2 == 0} # returns [1, 3]

As Andrew noted, it’s also commonly used for opening files and many other places. Basically anytime you have a standard function that could use some custom logic (like sorting an array or processing a file), you’ll use a block. There are other uses too, but this answer is already so long I’m afraid it will cause heart attacks in readers with weaker constitutions. Hopefully this clears up the confusion on this topic.

Add Comment