Nothing.to_json yields nil
Nothing works with ===
Add #__fetch__ to Maybe().proxy to get the wrapped value.
BREAKING CHANGES
Rename Either#else to Either#or to be consistent with Maybe
Using Either#else will give you a deprecation warning for now, it will be removed in v1.0
New Validation#fill method for validating filling Structs.
ExampleStruct = Struct.new(:a, :b)
module ExampleValidator
extend self
def a(params); Try { params[0] }.or 'a cannot be empty'; end
def b(params); Try { params[1] }.or 'b cannot be empty'; end
end
result = Validation.fill(ExampleStruct, [1, 2], ExampleValidator) == Sucess
example = result.fetch
example.a == 1
example.b == 2
Extend Try to also support lambdas.
The first parameter of Try can be used as a predicate and the block as formatter
Try(true) == Success(true)
Try(false) { "string" } == Failure("string")
Try(false) { "success" }.else("fail") == Failure("fail")
Maybe supports #proxy to avoid naming clashes between the underlying value and Maybe itself.
Maybe({a: 1}).proxy.fetch(:a) == Maybe(1)
# this is in effect syntactic sugar for
Maybe({a: 1}).map {|e| e.fetch(:a) }
Nothing#or coerces the or value into a Maybe, thus
Maybe(nil).or(1) == Just(1)
Maybe(nil).or(nil) == Nothing
Either coerces now Just and Nothing into Success and Failure
Either(Just.new(1)) == Success(1)
Either(Nothing) == Failure(Nothing)
Add the Try helper which works similar to Either, but takes a block
Try { Date.parse('2012-02-30') } == Failure
Try { Date.parse('2012-02-28') } == Success
Either else now supports also a block
Failure(1).else {|other| 1 + 2 } == Failure(3)
#or returns for Nothing an alternative
Maybe(nil).or(1) == 1
Maybe(1).or(2) == 1
BREAKING CHANGES
#to_s returns the inner value converted to string
Nothing has no method #name ... will need to use BasicObject
BREAKING CHANGES
Monad#map does not recognize Enumerables automatically anymore. It just maps, as a Functor.
Instead the new Monad#flat_map function operates on the underlying value as on Enumerable.
Either#else allows to exchange the inner value of Nothing to an alternative value.
Either(false == true).else('false was not true') == Failure(false was not true)
Success('truth needs no sugar coating').or('all lies') == Success('truth needs no sugar coating')
Either() coerces only StandardError to Failure, other exceptions higher in the hierarchy are will break the flow.
Thanks to @pithyless for the suggestion.
Monad is now a Module instead of a Class as previously. This fits the Monad metaphor better. Each monad must now implement the unit method itself, which is the correct way to do anyway.
You can now chain Eithers with + without providing a block or a proc:
Success(1) + Failure(2) == Failure(2)
Failure(1) + Failure(2) == Failure(1)
All monads now have the #to_ary and #to_a method, which coerces the inner value into an Array.
I am considering the Api now almost stable, the only thing I am not so sure about, is whether to apply the magic coercion or not.
Validation() now returns the successfully validated values.
See examples/validation.rb
and examples/validation_module
Implements the #map method for all Monads. It works on value types and on Enumerable collections.
Provide a proc or a block and it will return a transformed value or collection boxed back in the monad.
Monad.unit('FOO').map(&:capitalize).map {|v| "Hello #{v}"} == Monad(Hello Foo)
Add the Elvis operator _? - ruby does not allow ?: as operator and use it like the excellent andand
nil._? == Nothing
"foo"._? == 'foo'
{}._?.a.b == Nothing
{}._?[:foo] == Nothing
Contains Breaking Changes
Refactoring to use internal Monad class, from which all monads inherit.
Reimplemented Maybe as inherited class from Monad. The old Option implementation has been removed, maybe does the same though, but the code is much cleaner and obeys the 3 monadic laws.
Removed Maybe#fetch call with block`
Either and Validation are now in the Monadic namespace.
Removed the #chain method alias for bind in Either.
Moar examples with instance variables in an Either.chain.
Add monadic Validation, which is a special application (an applicative functor) of the Either Monad e.g.
Validation() do
check { check_age.(person.age); }
check { check_sobriety.(person.sobriety) }
end
It returns a Success([]) with an empty list, or a Failure([..]) with a list of all Failures.
To be more idiomatic rubyuesque, rename #value to #fetch, which throws now an NoValueError.
Thanks to @pithyless for the suggestion.
It now supports the Either monad, e.g.
either = Success(0).
bind { Success(1) }.
bind { Failure(2) }.
bind { Success(3) }
either == Failure(2) # the third bind is NOT executed
Some#map and Some#select accept proc and block, you can now use:
Option("FOO").map(&:downcase) # NEW
Option("FOO").map { |e| e.downcase } # old
Option("FOO").downcase # old
Removed #none?, please use #empty? instead.
Some and None are now in the Monadic namespace, however they are aliased when requiring monadic
