<commit_before># Monadic

helps dealing with exceptional situations, it comes from the sphere of functional programming and bringing the goodies I have come to love in Scala to my ruby projects (hence I will be using more Scala like constructs than Haskell).

My motivation to create this gem was that I often work with nested Hashes and need to reach deeply inside of them so my code is sprinkled with things like some_hash.fetch(:one, {}).fetch(:two, {}).fetch(:three, "unknown"). 

We have the following monadics:

- Option (Maybe in Haskell)
- Either *planned

What's the point of using monads in ruby? To me it started with having a safe way to deal with nil objects and other exceptions.
Thus you contain the erroneous behaviour within a monad - an indivisible, impenetrable unit.

## Usage

### Option
Is an optional type, which helps to handle error conditions gracefully. The one thing to remember about option is: 'What goes into the Option, stays in the Option'. 


    Option(User.find(123)).name._         # ._ is a shortcut for .fetch 

    # if you prefer the alias Maybe instead of option
    Maybe(User.find(123)).name._

    # confidently diving into nested hashes
    Maybe({})[:a][:b][:c]                   == None
    Maybe({})[:a][:b][:c].fetch('unknown')  == None
    Maybe(a: 1)[:a]._                       == 1

Basic usage examples:

    # handling nil (None serves as NullObject)
    obj = nil
    Option(obj).a.b.c            == None

    # None stays None
    Option(nil)._                == "None"
    "#{Option(nil)}"             == "None"
    Option(nil)._("unknown")     == "unknown"
    Option(nil).empty?           == true
    Option(nil).truly?           == false

    # Some stays Some, unless you unbox it
    Option('FOO').downcase       == Some('foo') 
    Option('FOO').downcase.fetch == "foo"
    Option('FOO').downcase._     == "foo"
    Option('foo').empty?         == false
    Option('foo').truly?         == true

Map, select:
    
    Option(123).map   { |value| User.find(value) } == Option(someUser)    # if user found
    Option(0).map     { |value| User.find(value) } == None                # if user not found
    Option([1,2]).map { |value| value.to_s }       == Option(["1", "2"])  # for all Enumerables

    Option('foo').select { |value| value.start_with?('f') } == Some('foo')
    Option('bar').select { |value| value.start_with?('f') } == None

Treat it like an array:

    Option(123).to_a         == [123]
    Option([123, 456]).to_a  == [123, 456]
    Option(nil)              == []

Falsey values (kind-of) examples:

    user = Option(User.find(123))
    user.name._

    user.fetch('You are not logged in') { |user| "You are logged in as #{user.name}" }.should == 'You are logged in as foo'

    if user != nil
      "You are logged in as foo"
    else
      "You are not logged in"

    user.subscribed?              # always true
    user.subscribed?.truly?       # true if subscribed is true
    user.subscribed?.fetch(false) # same as above
    user.subscribed?.or(false)    # same as above

Remember! an Option is never false (in Ruby terms), if you want to know if it is false, call `#empty?` of `#truly?`

`#truly?` will return true or false, always.

Slug example

    # instead of 
    def slug(title)
      if title
        title.strip.downcase.tr_s('^[a-z0-9]', '-')
      end
    end

    # or 

    def slug(title)
      title && title.strip.downcase.tr_s('^[a-z0-9]', '-')
    end

    # do it with a default
    def slug(title)
      Option(title).strip.downcase.tr_s('^[a-z0-9]', '-')._('unknown-title')
    end


see also

 * [Wikipedia Monad](See also http://en.wikipedia.org/wiki/Monad)
 * [Monad equivalend in Ruby](http://stackoverflow.com/questions/2709361/monad-equivalent-in-ruby)
 * [Option Type ](http://devblog.avdi.org/2011/05/30/null-objects-and-falsiness/)
 * [NullObject and Falsiness by @avdi](http://devblog.avdi.org/2011/05/30/null-objects-and-falsiness/)
 * [andand](https://github.com/raganwald/andand/blob/master/README.textile)
 * [ick](http://ick.rubyforge.org/)
 * [Monads in Ruby](http://moonbase.rydia.net/mental/writings/programming/monads-in-ruby/00introduction.html)
 * [The Maybe Monad in Ruby](http://pretheory.wordpress.com/2008/02/14/the-maybe-monad-in-ruby/)
 * [Monads in Ruby with nice syntax](http://www.valuedlessons.com/2008/01/monads-in-ruby-with-nice-syntax.html)
 * [Maybe in Ruby](https://github.com/bhb/maybe)
 * [Monads on the Cheap](http://osteele.com/archives/2007/12/cheap-monads)
 * [Rumonade](https://github.com/ms-ati/rumonade)
 * [Monads for functional programming](http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf)

## Installation<commit_msg>Add usage for Either to README<commit_after># Monadic

helps dealing with exceptional situations, it comes from the sphere of functional programming and bringing the goodies I have come to love in Scala to my ruby projects (hence I will be using more Scala like constructs than Haskell).

My motivation to create this gem was that I often work with nested Hashes and need to reach deeply inside of them so my code is sprinkled with things like some_hash.fetch(:one, {}).fetch(:two, {}).fetch(:three, "unknown"). 

We have the following monadics:

- Option (Maybe in Haskell) - Scala like with a rubyesque flavour
- Either - more Haskell like

What's the point of using monads in ruby? To me it started with having a safe way to deal with nil objects and other exceptions.
Thus you contain the erroneous behaviour within a monad - an indivisible, impenetrable unit.

## Usage

### Option
Is an optional type, which helps to handle error conditions gracefully. The one thing to remember about option is: 'What goes into the Option, stays in the Option'. 

    Option(User.find(123)).name._         # ._ is a shortcut for .fetch 

    # if you prefer the alias Maybe instead of option
    Maybe(User.find(123)).name._

    # confidently diving into nested hashes
    Maybe({})[:a][:b][:c]                   == None
    Maybe({})[:a][:b][:c].fetch('unknown')  == None
    Maybe(a: 1)[:a]._                       == 1

Basic usage examples:

    # handling nil (None serves as NullObject)
    obj = nil
    Option(obj).a.b.c            == None

    # None stays None
    Option(nil)._                == "None"
    "#{Option(nil)}"             == "None"
    Option(nil)._("unknown")     == "unknown"
    Option(nil).empty?           == true
    Option(nil).truly?           == false

    # Some stays Some, unless you unbox it
    Option('FOO').downcase       == Some('foo') 
    Option('FOO').downcase.fetch == "foo"
    Option('FOO').downcase._     == "foo"
    Option('foo').empty?         == false
    Option('foo').truly?         == true

Map, select:
    
    Option(123).map   { |value| User.find(value) } == Option(someUser)    # if user found
    Option(0).map     { |value| User.find(value) } == None                # if user not found
    Option([1,2]).map { |value| value.to_s }       == Option(["1", "2"])  # for all Enumerables

    Option('foo').select { |value| value.start_with?('f') } == Some('foo')
    Option('bar').select { |value| value.start_with?('f') } == None

Treat it like an array:

    Option(123).to_a         == [123]
    Option([123, 456]).to_a  == [123, 456]
    Option(nil)              == []

Falsey values (kind-of) examples:

    user = Option(User.find(123))
    user.name._

    user.fetch('You are not logged in') { |user| "You are logged in as #{user.name}" }.should == 'You are logged in as foo'

    if user != nil
      "You are logged in as foo"
    else
      "You are not logged in"

    user.subscribed?              # always true
    user.subscribed?.truly?       # true if subscribed is true
    user.subscribed?.fetch(false) # same as above
    user.subscribed?.or(false)    # same as above

Remember! an Option is never false (in Ruby terms), if you want to know if it is false, call `#empty?` of `#truly?`

`#truly?` will return true or false, always.

Slug example

    # instead of 
    def slug(title)
      if title
        title.strip.downcase.tr_s('^[a-z0-9]', '-')
      end
    end

    # or 

    def slug(title)
      title && title.strip.downcase.tr_s('^[a-z0-9]', '-')
    end

    # do it with a default
    def slug(title)
      Option(title).strip.downcase.tr_s('^[a-z0-9]', '-')._('unknown-title')
    end

### Either
Its main purpose here to handle errors gracefully, by chaining multiple calls in a functional way and stop evaluating them as soon as the first fails.
Assume you need several calls to construct some object in order to be useful, after each you need to check for success. Also you want to catch exceptions and not let them bubble upwards.

`Success` represents a successfull execution of an operation (Right in Scala, Haskell).  
`Failure` represents a failure to execute an operation (Left in Scala, Haskell).  

The `Either()` wrapper will treat `nil`, `false` or `empty?` as a `Failure` and all others as `Success`.

    result = parse_and_validate_params(params).
                bind ->(user_id) { User.find(user_id) }.        # if #find returns null it will become a Failure
                bind ->(user)    { authorized?(user); user }.   # if authorized? raises an Exception, it will be a Failure 
                bind ->(user)    { UserDecorator(user) }

    if result.success?
      @user = result.fetch                                      # result.fetch or result._ contains the 
      render 'page'
    else
      @error = result.fetch
      render 'error_page'
    end

You can use alternate syntaxes to achieve the same goal:

    # block and Haskell like >= operator
    Either(operation).
      >= { successful_method }.
      >= { failful_operation }

    # start with a Success, for instance a parameter
    Success('pzol').
      bind ->(previous) { good }.
      bind ->           { bad  }

    Either.chain do
      bind ->                   { good   }                     # >= is not supported for Either.chain, only bind
      bind ->                   { better }                     # better returns Success(some_int)
      bind ->(previous_result)  { previous_result + 1 }
    end

    either = Either(something)
    either += truth? Success('truth, only the truth') : Failure('lies, damn lies')

Exceptions are wrapped into a Failure:

    Either(true).
      bind -> { fail 'get me out of here' }                    # return a Failure(RuntimeError)

Another example:

    Success(params).
      bind ->(params)   { Either(params.fetch(:path)) }
      bind ->(path)     { load_stuff(params) }


## References

 * [Wikipedia Monad](See also http://en.wikipedia.org/wiki/Monad)
 * [Learn You a Haskell - for a few monads more](http://learnyouahaskell.com/for-a-few-monads-more)
 * [Monad equivalend in Ruby](http://stackoverflow.com/questions/2709361/monad-equivalent-in-ruby)
 * [Option Type ](http://devblog.avdi.org/2011/05/30/null-objects-and-falsiness/)
 * [NullObject and Falsiness by @avdi](http://devblog.avdi.org/2011/05/30/null-objects-and-falsiness/)
 * [andand](https://github.com/raganwald/andand/blob/master/README.textile)
 * [ick](http://ick.rubyforge.org/)
 * [Monads in Ruby](http://moonbase.rydia.net/mental/writings/programming/monads-in-ruby/00introduction.html)
 * [The Maybe Monad in Ruby](http://pretheory.wordpress.com/2008/02/14/the-maybe-monad-in-ruby/)
 * [Monads in Ruby with nice syntax](http://www.valuedlessons.com/2008/01/monads-in-ruby-with-nice-syntax.html)
 * [Maybe in Ruby](https://github.com/bhb/maybe)
 * [Monads on the Cheap](http://osteele.com/archives/2007/12/cheap-monads)
 * [Rumonade](https://github.com/ms-ati/rumonade)
 * [Monads for functional programming](http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf)

## Installation
{'fraction_non_alphanumeric': 0.1342828077314344, 'fraction_numerical': 0.015005086469989827, 'mean_word_length': 2.7167611846250788, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 18, 'https://': 6, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 1, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2668103', 'n_tokens_mistral': 3888, 'n_tokens_neox': 3593, 'n_words': 1122}