lib/zx/maybe.rb in zx-monads-0.0.5 vs lib/zx/maybe.rb in zx-monads-0.0.6

- old
+ new

@@ -1,34 +1,199 @@ # frozen_string_literal: true module Zx - module Maybe - module Maybeable - None = ->(*kwargs) { ::Maybe::None.new(*kwargs) } - Some = ->(*kwargs) { ::Maybe::Some.new(*kwargs) } - Maybe = ->(*kwargs) { ::Maybe.of(*kwargs) } + class Maybe + attr_reader :value + IsBlank = ->(value) { value.nil? || value.to_s.strip&.empty? || !value } + + def self.of(...) + new.of(...) + end + + def self.[](...) + of(...) + end + + def of(value) + return None.new if IsBlank[value] + + Some.new(value) + rescue StandardError + None.new + end + + def type + to_s.downcase.to_sym + end + + def some? + type == :some + end + + def none? + type == :none + end + + def unwrap + @value + end + + def or(value) + IsBlank[@value] ? value : @value + end + + def >>(other) + self > other + end + alias | >> + + def fmap(_) + self + end + + def >(_other) + self + end + + def map(arg = nil, &block) + return Maybe[block.call(@value)] if block_given? + return Maybe[arg.arity > 1 ? arg.curry.call(@value) : arg.call(@value)] if arg.respond_to?(:call) + + case arg + in None then self + in Symbol | String then dig(arg) + end + rescue StandardError => e + None.new(e.message) + end + alias apply map + + def map!(&block) + @value = block.call(@value) + + Maybe[@value] + end + + def apply!(...) + apply(...).unwrap + end + + def dig(...) + Maybe[@value&.dig(...)] + end + + def dig!(...) + dig(...).unwrap + end + + def match(some:, none:) + case self + in Some then some.call(@value) + else none.call + end + end + + def on_success(&block) + return self if none? + + block.call(Some[@value]) + + self + end + + def on_failure(&block) + return self if some? + + block.call(None[@value]) + + self + end + + def on(ontype, &block) + case ontype.to_sym + when :success then on_success(&block) + when :failure then on_failure(&block) + end + end + + class Some < Maybe + def self.[](...) + new(...) + end + + def initialize(value = nil) + @value = value + end + + def deconstruct + [@value] + end + + def inspect + format("#<Zx::Maybe::#{self}:0x%x value=%s>", object_id, @value.inspect) + end + + def to_s + 'Some' + end + + def >(other) + other.respond_to?(:call) ? other.call(@value) : other + end + + def fmap(&block) + Maybe[block.call(@value)] + end + end + + class None < Maybe + def self.[](...) + new(...) + end + + def initialize(value = nil) + @value = value + end + + def deconstruct + [nil] + end + + def inspect + format("#<Zx::Maybe::#{self}:0x%x value=%s>", object_id, @value.inspect) + end + + def to_s + 'None' + end + + def map + self + end + end + + module ClassMethods + None = ->(*kwargs) { Zx::Maybe::None.new(*kwargs) } + Some = ->(*kwargs) { Zx::Maybe::Some.new(*kwargs) } + Maybe = ->(*kwargs) { Zx::Maybe.of(*kwargs) } + def Maybe(*kwargs) - ::Maybe.of(*kwargs) + Zx::Maybe.of(*kwargs) end def Some(*kwargs) - ::Maybe::Some.new(*kwargs) + Zx::Maybe::Some.new(*kwargs) end def None(*kwargs) - ::Maybe::None.new(*kwargs) + Zx::Maybe::None.new(*kwargs) end def Try(default = nil, options = {}) - Some yield + Some[yield] rescue StandardError => e None[default || options.fetch(:or, nil)] end - end - - def self.included(klass) - klass.include(Maybeable) - klass.extend(Maybeable) end end end