Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Experimental Ruby exhaustive conditionals that raise if no branch matches. A bit like in e.g. Elm.
# This replaces conditions like: `case …; when …; when …; else raise "Nothing matched!"` if an exact match is all you need.
def cond(input, hash)
hash.fetch(input) { raise "No branch for #{input.inspect}!" }.call
end
input = :good
cond(input,
good: -> { puts "So good!" },
bad: -> { puts "So bad!" },
)
# Outputs "So good!"
input = :ugly
cond(input,
good: -> { puts "So good!" },
bad: -> { puts "So bad!" },
)
# Raises "No branch for :ugly!"
# This replaces conditions like: `case …; when …; when …; else raise "Nothing matched!"` with the same `===` matching as Ruby case statements.
def cond2(input, hash)
hash.each do |if_match, then_this|
return then_this.call if if_match === input
end
raise "No branch for #{input.inspect}!"
end
input = 123
cond2(
input,
101..200 => ->{ puts "So low!" },
201..300 => ->{ puts "So high!" },
)
# Outputs "So low!"
input = 666
cond2(
input,
101..200 => ->{ puts "So low!" },
201..300 => ->{ puts "So high!" },
)
# Raises "No branch for 666!"
# This replaces conditions like: `if …; elsif …; else raise "Nothing matched!"`
def cond3(hash)
hash.each do |if_this, then_that|
return then_that.call if if_this.call
end
raise "Exhausted list!"
end
number = 123
cond3(
->{ number.odd? } => ->{ puts "So odd!" },
->{ number.even? } => ->{ puts "So even!" },
)
# Outputs "So odd!"
number = 42
cond3(
->{ number > 1000 } => ->{ puts "So major!" },
->{ number > 100 } => ->{ puts "So minor!" },
)
# Raises "Exhausted list!"

Due to the limitations of Ruby metaprogramming (unlike e.g. Elixir macros), we have to use lambdas for the "then" cases unless we want to evaluate them even if that branch doesn't match.

And in example 3, we also need it for the "if" cases, unless we're OK with evaluating conditions prematurely.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment