Definition
module Result
module Callable
def [](*args)
new(*args)
end
end
class Ok < SimpleDelegator
extend Callable
def map!(callable = nil, &block)
block ||= callable if callable.respond_to?(:call)
result = block.call(unwrap) if block
[Ok, Err].include?(result.class) ? result : Ok[result]
end
def map(callable = nil, &block)
map!(callable, &block)
rescue => exception
Err[exception]
end
alias :unwrap! :__getobj__
end
class Err
extend Callable
def initialize(message, exception = StandardError)
@exception = exception.new(message)
end
def map(_)
self # chainable
end
def map!
raise @exception
end
alias :unwrap! :map!
end
end
Usage
module FromStr
extend self # one-method module
include Result # just to write Err instead of Result::Err
def [](number)
num = number.to_s.to_i
return Err["Not a number", TypeError] unless num.to_s == number.to_s
return Err["Negative", ArgumentError] if num.negative?
return Err["Too big", ArgumentError] unless num < 64
Ok[num]
end
end
# u8::from_str("7") # => Ok(7)
# .map(|x| x * 5) # => Ok(35)
# .unwrap() # => 35
FromStr["7"] # => Ok[7]
.map { |x| x * 5 } # => Ok[35]
.unwrap! # => 37
# u8::from_str("foo") # => Err(ParseIntError { kind: InvalidDigit })
# .map(|x| x * 5) # => Err(ParseIntError { kind: InvalidDigit })
# .unwrap() # => thread 'main' panicked at 'called `Result::unwrap()`
# # on an `Err` value: ParseIntError { kind: InvalidDigit }'
FromStr["foo"] # => Err["Not a number", TypeError]
.map { |x| x * 5 } # => still Err["Not a number", TypeError]
.unwrap! # => raises TypeError "Not a number"