philiprehberger-maybe

Tests Gem Version Last updated

Optional/Maybe monad with safe chaining and pattern matching

Requirements

  • Ruby >= 3.1

Installation

Add to your Gemfile:

gem "philiprehberger-maybe"

Or install directly:

gem install philiprehberger-maybe

Usage

require "philiprehberger/maybe"

result = Philiprehberger::Maybe.wrap(42)
result.value  # => 42
result.some?  # => true

none = Philiprehberger::Maybe.wrap(nil)
none.none?  # => true

Safe Chaining

result = Philiprehberger::Maybe.wrap(user)
  .map { |u| u.address }
  .map { |a| a.city }
  .or_else('Unknown')

Pattern Matching

case Philiprehberger::Maybe.wrap(value)
in { some: true, value: Integer => v }
  puts "Got integer: #{v}"
in { none: true }
  puts 'No value'
end

Filtering

Philiprehberger::Maybe.wrap(18)
  .filter { |v| v >= 21 }
  .or_else(0)
  .value  # => 0

Digging into Nested Structures

data = { user: { address: { city: 'Vienna' } } }
Philiprehberger::Maybe.wrap(data)
  .dig(:user, :address, :city)
  .value  # => 'Vienna'

Flat Map

Philiprehberger::Maybe.wrap(5)
  .flat_map { |v| Philiprehberger::Maybe.wrap(v > 0 ? v : nil) }
  .value  # => 5

Combining with Zip

a = Philiprehberger::Maybe.wrap(1)
b = Philiprehberger::Maybe.wrap(2)
a.zip(b).value  # => [1, 2]

c = Philiprehberger::Maybe.wrap(nil)
a.zip(c).none?  # => true

Recovery

Philiprehberger::Maybe.wrap(nil)
  .recover { 'default' }
  .value  # => 'default'

Side Effects with Tap

Philiprehberger::Maybe.wrap(42)
  .tap { |v| puts "Got: #{v}" }
  .map { |v| v * 2 }
  .value  # => 84

Enumerable Support

Philiprehberger::Maybe.wrap(42).to_a     # => [42]
Philiprehberger::Maybe.wrap(nil).to_a    # => []

Philiprehberger::Maybe.wrap(5)
  .select { |v| v > 3 }  # => [5]

Utility Methods

a = Philiprehberger::Maybe.wrap(1)
b = Philiprehberger::Maybe.wrap(2)
Philiprehberger::Maybe.all?(a, b)        # => true

c = Philiprehberger::Maybe.wrap(nil)
Philiprehberger::Maybe.first_some(c, a)  # => Some(1)

API

Maybe

Method Description
.wrap(value) Wrap a value in Some (non-nil) or None (nil)
.all?(*maybes) Return true if all arguments are Some
.first_some(*maybes) Return the first Some, or None if all are None

Maybe::Some

Method Description
#value Return the wrapped value
#some? Return true
#none? Return false
`#map { \ v\
`#flat_map { \ v\
`#filter { \ v\
#or_else(default) Return self (ignores default)
#or_raise(error, msg) Return the value
#dig(*keys) Dig into nested hashes/arrays
#zip(*others) Combine Maybes; Some array if all Some, else None
`#tap { \ v\
`#each { \ v\
#deconstruct_keys(keys) Pattern matching support

Maybe::None

Method Description
#value Return nil
#some? Return false
#none? Return true
`#map { \ v\
`#flat_map { \ v\
`#filter { \ v\
#or_else(default) Return default wrapped in Maybe
#or_raise(error, msg) Raise the specified error
#dig(*keys) Return None
#recover { } Convert None to Some via block
#zip(*others) Return None (always)
`#tap { \ v\
`#each { \ v\
#deconstruct_keys(keys) Pattern matching support

Development

bundle install
bundle exec rspec
bundle exec rubocop

Support

If you find this project useful:

Star the repo

🐛 Report issues

💡 Suggest features

❤️ Sponsor development

🌐 All Open Source Projects

💻 GitHub Profile

🔗 LinkedIn Profile

License

MIT