Fear
This gem provides Option, Either, and Try monads implemented an idiomatic way.
It is highly inspired by scala's implementation.
Installation
Add this line to your application's Gemfile:
gem 'fear'
And then execute:
$ bundle
Or install it yourself as:
$ gem install fear
Usage
Option
Represents optional values. Instances of Option are either an instance of
Some or the object None.
The most idiomatic way to use an Option instance is to treat it
as a collection and use map, flat_map, select, or each:
include Fear::Option::Mixin
name = Option(params[:name])
upper = name.map(&:strip).select { |n| n.length != 0 }.map(&:upcase)
puts upper.get_or_else('')
This allows for sophisticated chaining of Option values without
having to check for the existence of a value.
See full documentation Fear::Option
Try
The Try represents a computation that may either result in an exception,
or return a successfully computed value. Instances of Try, are either
an instance of Success or Failure.
For example, Try can be used to perform division on a user-defined input,
without the need to do explicit exception-handling in all of the places
that an exception might occur.
include Fear::Try::Mixin
dividend = Try { Integer(params[:dividend]) }
divisor = Try { Integer(params[:divisor]) }
problem = dividend.flat_map { |x| divisor.map { |y| x / y }
if problem.success?
puts "Result of #{dividend.get} / #{divisor.get} is: #{problem.get}"
else
puts "You must've divided by zero or entered something wrong. Try again"
puts "Info from the exception: #{problem.exception.message}"
end
See full documentation Fear::Try
Either
Represents a value of one of two possible types (a disjoint union.)
An instance of Either is either an instance of Left or Right.
A common use of Either is as an alternative to Option for dealing
with possible missing values. In this usage, None is replaced
with a Left which can contain useful information.
Right takes the place of Some. Convention dictates
that Left is used for failure and Right is used for success.
For example, you could use Either<String, Fixnum> to select whether a
received input is a String or an Fixnum.
include Fear::Either::Mixin
input = Readline.readline('Type Either a string or an Int: ', true)
result = begin
Right(Integer(input))
rescue ArgumentError
Left(input)
end
puts(
result.reduce(
-> (x) { "You passed me the Int: #{x}, which I will increment. #{x} + 1 = #{x+1}" },
-> (x) { "You passed me the String: #{x}" }
)
)
See full documentation Fear::Either
For composition
Provides syntactic sugar for composition of multiple monadic operations.
It supports two such operations - flat_map and map. Any class providing them
is supported by For.
include Fear::For::Mixin
For(a: Some(2), b: Some(3)) do
a * b
end #=> Some(6)
It would be translated to
Some(2).flat_map do |a|
Some(3).map do |b|
a * b
end
end
If one of operands is None, the result is None
For(a: Some(2), b: None()) do
a * b
end #=> None()
For(a: None(), b: Some(2)) do
a * b
end #=> None()
For works with arrays as well
For(a: [1, 2], b: [2, 3], c: [3, 4]) do
a * b * c
end #=> [6, 8, 9, 12, 12, 16, 18, 24]
would be translated to:
[1, 2].flat_map do |a|
[2, 3].flat_map do |b|
[3, 4].map do |c|
a * b * c
end
end
end
Testing
To simplify testing, you may use fear-rspec gem. It provides a bunch of rspec matchers.
Contributing
- Fork it ( https://github.com/bolshakov/fear/fork )
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request