lib/rumonade/error_handling.rb
require "delegate"
module Rumonade
# A partial function is a unary function where the domain does not necessarily include all values.
# The function {#defined_at?} allows to test dynamically if a value is in the domain of the function.
#
# NOTE: This is only here to mimic the Scala library just enough to allow a close translation of the exception
# handling functionality. It's not because I'm the sort that just loves pure functional idioms so damn much for
# their own sake. Just FYI.
class PartialFunction < DelegateClass(Proc)
def initialize(defined_at_proc, call_proc)
super(call_proc)
@defined_at_proc = defined_at_proc
end
# Checks if a value is contained in the function's domain.
# @param x the value to test
# @return [Boolean] Returns +true+, iff +x+ is in the domain of this function, +false+ otherwise.
def defined_at?(x)
@defined_at_proc.call(x)
end
# Composes this partial function with a fallback partial function which
# gets applied where this partial function is not defined.
# @param [PartialFunction] other the fallback function
# @return [PartialFunction] a partial function which has as domain the union of the domains
# of this partial function and +other+. The resulting partial function takes +x+ to +self.call(x)+
# where +self+ is defined, and to +other.call(x)+ where it is not.
def or_else(other)
PartialFunction.new(lambda { |x| self.defined_at?(x) || other.defined_at?(x) },
lambda { |x| if self.defined_at?(x) then self.call(x) else other.call(x) end })
end
# Composes this partial function with a transformation function that
# gets applied to results of this partial function.
# @param [Proc] func the transformation function
# @return [PartialFunction] a partial function with the same domain as this partial function, which maps
# arguments +x+ to +func.call(self.call(x))+.
def and_then(func)
PartialFunction.new(@defined_at_proc, lambda { |x| func.call(self.call(x)) })
end
end
# Classes representing the components of exception handling.
# Each class is independently composable. Some example usages:
#
# require "rumonade"
# require "uri"
#
# s = "http://"
# x1 = catching(URI::InvalidURIError).opt { URI.parse(s) }
# x2 = catching(URI::InvalidURIError, NoMethodError).either { URI.parse(s) }
#
module ErrorHandling
# Should re-raise exceptions like +Interrupt+ and +NoMemoryError+ by default.
# @param [Exception] ex the exception to consider re-raising
# @return [Boolean] Returns +true+ if a subclass of +StandardError+, otherwise +false+.
def should_reraise?(ex)
case ex
when StandardError; false
else true
end
end
end
end