ruby-blocks-procs-lambdas

Ruby Blocks, Procs, and Lambdas

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "ruby-blocks-procs-lambdas" with this command: npx skills add thebushidocollective/han/thebushidocollective-han-ruby-blocks-procs-lambdas

Ruby Blocks, Procs, and Lambdas

Master Ruby's functional programming features with blocks, procs, and lambdas. These are fundamental to Ruby's expressive and elegant style.

Blocks

Basic Block Syntax

Block with do...end (multi-line)

[1, 2, 3].each do |num| puts num * 2 end

Block with {...} (single line)

[1, 2, 3].each { |num| puts num * 2 }

Yielding to Blocks

def repeat(times) times.times do yield # Execute the block end end

repeat(3) { puts "Hello" }

With block parameters

def greet yield("World") end

greet { |name| puts "Hello, #{name}!" }

Block Arguments

def process_data(data) result = yield(data) puts "Result: #{result}" end

process_data(10) { |x| x * 2 } # Result: 20

Checking for Blocks

def optional_block if block_given? yield else puts "No block provided" end end

optional_block { puts "Block executed" } optional_block

Block Local Variables

x = 10

[1, 2, 3].each do |num; local_var| local_var = num * 2 # local_var only exists in block puts local_var end

puts x # 10 (unchanged)

Procs

Creating Procs

Using Proc.new

my_proc = Proc.new { |x| x * 2 } puts my_proc.call(5) # 10

Using proc method (deprecated in some versions)

my_proc = proc { |x| x * 2 }

Using -> (stabby lambda syntax for Proc)

my_proc = ->(x) { x * 2 }

Proc Characteristics

Procs don't care about argument count

flexible_proc = Proc.new { |x, y| "x: #{x}, y: #{y}" } puts flexible_proc.call(1) # x: 1, y: puts flexible_proc.call(1, 2, 3) # x: 1, y: 2 (ignores extra)

Procs return from the enclosing method

def proc_return my_proc = Proc.new { return "from proc" } my_proc.call "after proc" # Never reached end

puts proc_return # "from proc"

Passing Procs as Arguments

def execute_proc(my_proc) my_proc.call end

greeting = Proc.new { puts "Hello from proc!" } execute_proc(greeting)

Converting Blocks to Procs

def method_with_proc(&block) block.call end

method_with_proc { puts "Block converted to proc" }

Lambdas

Creating Lambdas

Using lambda keyword

my_lambda = lambda { |x| x * 2 }

Using -> (stabby lambda)

my_lambda = ->(x) { x * 2 }

Multi-line stabby lambda

my_lambda = ->(x) do result = x * 2 result + 1 end

puts my_lambda.call(5) # 11

Lambda Characteristics

Lambdas enforce argument count

strict_lambda = ->(x, y) { x + y }

strict_lambda.call(1) # ArgumentError

strict_lambda.call(1, 2) # Works

Lambdas return to the caller

def lambda_return my_lambda = -> { return "from lambda" } my_lambda.call "after lambda" # This IS reached end

puts lambda_return # "after lambda"

Lambda with Multiple Arguments

add = ->(x, y) { x + y } multiply = ->(x, y, z) { x * y * z }

puts add.call(3, 4) # 7 puts multiply.call(2, 3, 4) # 24

Default arguments

greet = ->(name = "World") { "Hello, #{name}!" } puts greet.call # "Hello, World!" puts greet.call("Ruby") # "Hello, Ruby!"

Proc vs Lambda

Argument handling

my_proc = Proc.new { |x, y| puts "x: #{x}, y: #{y}" } my_lambda = ->(x, y) { puts "x: #{x}, y: #{y}" }

my_proc.call(1) # Works: x: 1, y:

my_lambda.call(1) # ArgumentError

Return behavior

def test_return proc_test = Proc.new { return "proc return" } lambda_test = -> { return "lambda return" }

proc_test.call # Returns from method "end" # Never reached end

def test_lambda lambda_test = -> { return "lambda return" } lambda_test.call # Returns from lambda "end" # This IS reached end

Check if it's a lambda

my_proc = Proc.new { } my_lambda = -> { }

puts my_proc.lambda? # false puts my_lambda.lambda? # true

Closures

def multiplier(factor) ->(x) { x * factor } end

times_two = multiplier(2) times_three = multiplier(3)

puts times_two.call(5) # 10 puts times_three.call(5) # 15

Closures capture variables

def counter count = 0

increment = -> { count += 1 } decrement = -> { count -= 1 } value = -> { count }

[increment, decrement, value] end

inc, dec, val = counter inc.call inc.call puts val.call # 2 dec.call puts val.call # 1

Method Objects

class Calculator def add(x, y) x + y end end

calc = Calculator.new add_method = calc.method(:add) puts add_method.call(3, 4) # 7

Converting methods to procs

add_proc = calc.method(:add).to_proc puts add_proc.call(5, 6) # 11

Symbol to Proc

& converts symbol to proc

numbers = [1, 2, 3, 4, 5]

These are equivalent:

numbers.map { |n| n.to_s } numbers.map(&:to_s)

Works with any method

["hello", "world"].map(&:upcase) # ["HELLO", "WORLD"] [1, 2, 3].select(&:even?) # [2]

Higher-Order Functions

def compose(f, g) ->(x) { f.call(g.call(x)) } end

double = ->(x) { x * 2 } square = ->(x) { x * x }

double_then_square = compose(square, double) puts double_then_square.call(3) # 36 (3 * 2 = 6, 6 * 6 = 36)

Currying

Manual currying

add = ->(x) { ->(y) { x + y } } add_five = add.call(5) puts add_five.call(3) # 8

Built-in currying

multiply = ->(x, y, z) { x * y * z } curried = multiply.curry times_two = curried.call(2) times_two_three = times_two.call(3) puts times_two_three.call(4) # 24

Partial application

puts curried.call(2, 3).call(4) # 24

Practical Patterns

Lazy Evaluation

def lazy_value puts "Computing expensive value..." 42 end

Wrap in lambda for lazy evaluation

lazy = -> { lazy_value }

puts "Before call" result = lazy.call # Only computed here puts result

Callback Pattern

class Button def initialize @on_click = [] end

def on_click(&block) @on_click << block end

def click @on_click.each(&:call) end end

button = Button.new button.on_click { puts "Button clicked!" } button.on_click { puts "Another handler" } button.click

Strategy Pattern

class Sorter def initialize(strategy) @strategy = strategy end

def sort(array) @strategy.call(array) end end

ascending = ->(arr) { arr.sort } descending = ->(arr) { arr.sort.reverse }

sorter = Sorter.new(ascending) puts sorter.sort([3, 1, 2]) # [1, 2, 3]

sorter = Sorter.new(descending) puts sorter.sort([3, 1, 2]) # [3, 2, 1]

Memoization

def memoize(&block) cache = {} ->(arg) do cache[arg] ||= block.call(arg) end end

expensive_operation = memoize do |n| puts "Computing for #{n}..." n * n end

puts expensive_operation.call(5) # Computing for 5... 25 puts expensive_operation.call(5) # 25 (cached)

Best Practices

  • Use blocks for simple iteration and single-use closures

  • Use lambdas for strict argument checking and returnable closures

  • Use procs for flexible argument handling (rare cases)

  • Prefer -> syntax for lambdas (more concise)

  • Use &:symbol for simple method calls on collections

  • Leverage closures for encapsulation and data privacy

  • Use block_given? before yielding to optional blocks

Anti-Patterns

❌ Don't use Proc.new for strict behavior - use lambda instead ❌ Don't ignore return behavior - understand proc vs lambda differences ❌ Don't overuse closures - can lead to memory leaks if not careful ❌ Don't create deeply nested lambdas - hard to read and debug ❌ Don't forget to handle missing blocks - check with block_given?

Related Skills

  • ruby-oop - For understanding method context

  • ruby-metaprogramming - For dynamic block/proc usage

  • ruby-standard-library - For Enumerable methods using blocks

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

android-jetpack-compose

No summary provided by upstream source.

Repository SourceNeeds Review
General

fastapi-async-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
General

storybook-story-writing

No summary provided by upstream source.

Repository SourceNeeds Review
General

atomic-design-fundamentals

No summary provided by upstream source.

Repository SourceNeeds Review