Skip to content

Instantly share code, notes, and snippets.

@henrik
Last active April 15, 2019 09:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save henrik/c59ba3fc8d98b5ec30a347248e16d8b3 to your computer and use it in GitHub Desktop.
Save henrik/c59ba3fc8d98b5ec30a347248e16d8b3 to your computer and use it in GitHub Desktop.
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