Skip to content

Instantly share code, notes, and snippets.

@hmaddocks
Last active January 25, 2025 20:58
Show Gist options
  • Save hmaddocks/d886298b600c128a46603b22f7ab3756 to your computer and use it in GitHub Desktop.
Save hmaddocks/d886298b600c128a46603b22f7ab3756 to your computer and use it in GitHub Desktop.
Implementation of a Result monad for Ruby
# frozen_string_literal: true
# Represents an immutable Result object that can contain either a successful value
# or an error message. This implements the Result pattern for handling success/failure
# scenarios in a functional way.
Result = Data.define(:value, :error_message, :creation_location) do
def self.ok(value)
new(value: value, error_message: nil, creation_location: caller_locations(1, 1).first)
end
def self.error(message)
new(value: nil, error_message: message, creation_location: caller_locations(1, 1).first)
end
def ok?
!value.nil?
end
def error?
!error_message.nil?
end
def ok
raise "Cannot get value from error result: #{error_message}" if error?
value
end
def ok_or(default_value)
value || default_value
end
def error
raise "Cannot get error from ok result" if ok?
error_message
end
def map
return self if error?
Result.ok(yield(ok))
rescue StandardError => e
Result.error(e.message)
end
def and_then
return self if error?
yield(ok)
end
def where
creation_location
end
private
def initialize(value:, error_message:, creation_location:)
raise ArgumentError, "Cannot have both value and error" if value && error_message
raise ArgumentError, "Must provide either value or error" if value.nil? && error_message.nil?
super
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment