How to Fix the Yield Command Will Not Work in Ruby

So there I was, reading The Well-Grounded Rubyist (a fantastic book, by the way!), trying to implement my own versions of Ruby’s each and map methods. The code looked simple enough. I added my_each to the Array class, and then wrote my_map to use my_each with a block. Here’s what I had:

class Array
  def my_each
    c = 0
    until c == size
      yield self[c]
      c += 1
    end
    self
  end

  def my_map
    acc = []
    my_each { |e| acc << yield e } # This line broke everything!
    acc
  end
end

But when I ran it, I got this infuriating error:

page185a.rb:14: syntax error, unexpected local variable or method, expecting '}'
my_each { |e| acc << yield e }

The error pointed to the yield e part. Why? I thought I followed the book exactly! I tried splitting the code into different lines, rewriting the block—nothing worked. Turns out, the issue was something I’d never considered: Ruby’s parser gets confused when yield is used with certain operators like <<.

The Fix That Saved My Sanity

After hours of Googling and yelling at my screen, I found the solution: wrap yield e in parentheses. Yes, really. That’s it.

Here’s the corrected my_map method:

def my_map
  acc = []
  my_each { |e| acc << (yield e) } # Parentheses fix the ambiguity!
  acc
end

Why does this work?
Ruby’s parser thought I was trying to call << yield as a method on e, which makes no sense. Adding parentheses clarifies that yield e is a single value being passed to <<. Lesson learned: when in doubt, add parentheses to yield in blocks!

Now, this works perfectly:

names = ["David", "Alan", "Black"]
p names.my_map { |name| name.upcase } # => ["DAVID", "ALAN", "BLACK"]

Leveling Up Adding More Methods

Once I fixed the yield issue, I decided to flex my new Ruby skills by adding more methods inspired by Ruby’s Enumerable module. Here’s what I built:

 my_map Without a Block Returns an Enumerator

Ruby methods like map return an Enumerator if no block is given. Let’s replicate that:

def my_map
  return to_enum(:my_map) unless block_given? # No block? Return Enumerator!
  acc = []
  my_each { |e| acc << (yield e) }
  acc
end

Example:

map_enum = [1, 2, 3].my_map
p map_enum.each { |n| n * 2 } # => [2, 4, 6]

In-Place my_map! Method

Why not modify the array directly, like map!?

def my_map!
  return to_enum(:my_map!) unless block_given?
  my_each_with_index { |e, i| self[i] = yield(e) } # Update the array in place
  self
end

Example:

numbers = [1, 2, 3]
numbers.my_map! { |n| n * 10 }
p numbers # => [10, 20, 30]

Filtering with my_select

Let’s filter elements based on a block condition:

def my_select
  return to_enum(:my_select) unless block_given?
  acc = []
  my_each { |e| acc << e if yield(e) } # Collect elements where the block is truthy
  acc
end

Example:

p [10, 20, 30, 40].my_select { |n| n > 25 } # => [30, 40]

The Swiss Army Knife: my_reduce

This one was tricky! I wanted to replicate reduce/inject, supporting both symbols (like :+) and blocks:

def my_reduce(initial = nil, sym = nil, &block)
  acc = initial.nil? ? first : initial # Handle initial value
  start_idx = initial.nil? ? 1 : 0

  if block
    my_each_with_index(start_idx...) { |e, i| acc = block.call(acc, e) }
  elsif sym
    my_each_with_index(start_idx...) { |e, i| acc = acc.send(sym, e) }
  else
    raise ArgumentError, "You must provide a block or a symbol"
  end

  acc
end

Examples:

p [1, 2, 3].my_reduce(:+) # => 6 (sum)
p [1, 2, 3].my_reduce(10, :+) # => 16 (sum with initial value)

Final Thoughts

  1. Parentheses Matter with yield: Ruby’s parser isn’t perfect. When combining yield with operators like <<, clarify your intent with (yield e).
  2. Emulate Ruby’s Patterns: Returning Enumerator objects when no block is given makes your methods more flexible and idiomatic.
  3. Iteration is Powerful: By building my_eachmy_mapmy_select, and my_reduce, I finally grasped how Ruby’s iteration methods work under the hood.

If you’re new to Ruby like me, don’t fear yield just remember to add those parentheses! And hey, try re implementing methods yourself.

Related blog posts