philiprehberger-maybe
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: