rumonade 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.rvmrc ADDED
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # This is an RVM Project .rvmrc file, used to automatically load the ruby
4
+ # development environment upon cd'ing into the directory
5
+
6
+ # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
7
+ # Only full ruby name is supported here, for short names use:
8
+ # echo "rvm use 1.9.3" > .rvmrc
9
+ environment_id="ruby-1.9.3-p385@rumonade"
10
+
11
+ # Uncomment the following lines if you want to verify rvm version per project
12
+ # rvmrc_rvm_version="1.18.8 (stable)" # 1.10.1 seams as a safe start
13
+ # eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
14
+ # echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
15
+ # return 1
16
+ # }
17
+
18
+ # First we attempt to load the desired environment directly from the environment
19
+ # file. This is very fast and efficient compared to running through the entire
20
+ # CLI and selector. If you want feedback on which environment was used then
21
+ # insert the word 'use' after --create as this triggers verbose mode.
22
+ if [[ -d "${rvm_path:-$HOME/.rvm}/environments"
23
+ && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
24
+ then
25
+ \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
26
+ [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] &&
27
+ \. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true
28
+ else
29
+ # If the environment file has not yet been created, use the RVM CLI to select.
30
+ rvm --create "$environment_id" || {
31
+ echo "Failed to create RVM environment '${environment_id}'."
32
+ return 1
33
+ }
34
+ fi
35
+
36
+ # If you use bundler, this might be useful to you:
37
+ # if [[ -s Gemfile ]] && {
38
+ # ! builtin command -v bundle >/dev/null ||
39
+ # builtin command -v bundle | GREP_OPTIONS= \grep $rvm_path/bin/bundle >/dev/null
40
+ # }
41
+ # then
42
+ # printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n"
43
+ # gem install bundler
44
+ # fi
45
+ # if [[ -s Gemfile ]] && builtin command -v bundle >/dev/null
46
+ # then
47
+ # bundle install | GREP_OPTIONS= \grep -vE '^Using|Your bundle is complete'
48
+ # fi
data/HISTORY.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # HISTORY
2
2
 
3
+ ## v0.4.1 (May 9, 2013)
4
+
5
+ - fixed behavior of #flatten called with optional depth parameter
6
+ - thanks Martin Mauch (@nightscape)!
7
+ - See full list @ https://github.com/ms-ati/rumonade/compare/v0.4.0...v0.4.1
8
+
3
9
  ## v0.4.0 (Nov 11, 2012)
4
10
 
5
11
  - added scala-like extensions to Hash
data/MIT-LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012 Marc Siegel
1
+ Copyright (c) 2011-2013 Marc Siegel
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md ADDED
@@ -0,0 +1,148 @@
1
+ # [Rumonade](https://rubygems.org/gems/rumonade)
2
+
3
+ [![Build Status](https://travis-ci.org/ms-ati/rumonade.png)](https://travis-ci.org/ms-ati/rumonade)
4
+ [![Dependency Status](https://gemnasium.com/ms-ati/rumonade.png)](https://gemnasium.com/ms-ati/rumonade)
5
+
6
+ *_Project_*: [github](http://github.com/ms-ati/rumonade)
7
+
8
+ *_Documentation_*: [rubydoc.info](http://rubydoc.info/gems/rumonade/frames)
9
+
10
+ ## A [Ruby](http://www.ruby-lang.org) [Monad](http://en.wikipedia.org/wiki/Monad_\(functional_programming\)) Library, Inspired by [Scala](http://www.scala-lang.org)
11
+
12
+ Are you working in both the [Scala](http://www.scala-lang.org) and [Ruby](http://www.ruby-lang.org) worlds,
13
+ and finding that you miss some of the practical benefits of Scala's
14
+ [monads](http://james-iry.blogspot.com/2007/09/monads-are-elephants-part-1.html) in Ruby?
15
+ If so, then Rumonade is for you.
16
+
17
+ The goal of this library is to make the most common and useful Scala monadic idioms available in Ruby via the following classes:
18
+ * [Option](http://rubydoc.info/gems/rumonade/Rumonade/Option)
19
+ * [Array](http://rubydoc.info/gems/rumonade/Rumonade/ArrayExtensions)
20
+ * [Either](http://rubydoc.info/gems/rumonade/Rumonade/Either)
21
+ * [Hash](http://rubydoc.info/gems/rumonade/Rumonade/Hash)
22
+ * _more coming soon_
23
+
24
+ Syntactic support for scala-like [for-comprehensions](http://www.scala-lang.org/node/111) will be implemented
25
+ as a sequence of calls to `flat_map`, `select`, etc, modeling [Scala's
26
+ approach](http://stackoverflow.com/questions/3754089/scala-for-comprehension/3754568#3754568).
27
+
28
+ Support for an [all_catch](http://www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/library/scala/util/control/Exception$.html)
29
+ idiom will be implemented to turn blocks which might throw exceptions into Option or Either
30
+ results. If this proves useful (and a good fit for Ruby), then more narrow functional catchers can be implemented as well.
31
+
32
+ ## Usage Examples
33
+
34
+ ### Option: handle _possibly_ _nil_ values in a _functional_ fashion:
35
+
36
+ ```ruby
37
+ def format_date_in_march(time_or_date_or_nil)
38
+ Option(time_or_date_or_nil). # wraps possibly-nil value in an Option monad (Some or None)
39
+ map(&:to_date). # transforms a contained Time value into a Date value
40
+ select {|d| d.month == 3}. # filters out non-matching Date values (Some becomes None)
41
+ map(&:to_s). # transforms a contained Date value into a String value
42
+ map {|s| s.gsub('-', '')}. # transforms a contained String value by removing '-'
43
+ get_or_else("not in march!") # returns the contained value, or the alternative if None
44
+ end
45
+
46
+ format_date_in_march(nil) # => "not in march!"
47
+ format_date_in_march(Time.parse('2009-01-01 01:02')) # => "not in march!"
48
+ format_date_in_march(Time.parse('2011-03-21 12:34')) # => "20110321"
49
+ ```
50
+
51
+ Note:
52
+ * each step of the chained computations above are functionally isolated
53
+ * the value can notionally _start_ as nil, or _become_ nil during a computation, without effecting any other chained computations
54
+
55
+ ---
56
+ ### Either: handle failures (Left) and successes (Right) in a _functional_ fashion:
57
+
58
+ ```ruby
59
+ def find_person(name)
60
+ case name
61
+ when /Jack/i, /John/i
62
+ Right(name.capitalize)
63
+ else
64
+ Left("No such person: #{name.capitalize}")
65
+ end
66
+ end
67
+
68
+ # success looks like this:
69
+ find_person("Jack")
70
+ # => Right("Jack")
71
+
72
+ # failure looks like this:
73
+ find_person("Jill")
74
+ # => Left("No such person: Jill")
75
+
76
+ # lift the contained values into Array, in order to combine them:
77
+ find_person("Joan").lift_to_a
78
+ # => Left(["No such person: Joan"])
79
+
80
+ # on the 'happy path', combine and transform successes into a single success result:
81
+ (find_person("Jack").lift_to_a +
82
+ find_person("John").lift_to_a).right.map { |*names| names.join(" and ") }
83
+ # => Right("Jack and John")
84
+
85
+ # but if there were errors, we still have a Left with all the errors inside:
86
+ (find_person("Jack").lift_to_a +
87
+ find_person("John").lift_to_a +
88
+ find_person("Jill").lift_to_a +
89
+ find_person("Joan").lift_to_a).right.map { |*names| names.join(" and ") }
90
+ # => Left(["No such person: Jill", "No such person: Joan"])
91
+
92
+ # equivalent to the previous example, but shorter:
93
+ %w(Jack John Jill Joan).
94
+ map { |nm| find_person(nm).lift_to_a }.inject(:+).
95
+ right.map { |*names| names.join(" and ") }
96
+ # => Left(["No such person: Jill", "No such person: Joan"])
97
+ ```
98
+
99
+ Also, see the `Either` class in action in the [Ruby version](https://gist.github.com/2553490)
100
+ of [A Tale of Three Nightclubs](http://bugsquash.blogspot.com/2012/03/example-of-applicative-validation-in.html)
101
+ validation example in F#, and compare it to the [Scala version using scalaz](https://gist.github.com/970717).
102
+
103
+ ---
104
+ ### Hash: `flat_map` returns a Hash for each key/value pair; `get` returns an Option
105
+
106
+ ```ruby
107
+ h = { "Foo" => 1, "Bar" => 2, "Baz" => 3 }
108
+
109
+ h = h.flat_map { |k, v| { k => v, k.upcase => v * 10 } }
110
+ # => {"Foo"=>1, "FOO"=>10, "Bar"=>2, "BAR"=>20, "Baz"=>3, "BAZ"=>30}
111
+
112
+ h = h.select { |k, v| k =~ /^b/i }
113
+ # => {"Bar"=>2, "BAR"=>20, "Baz"=>3, "BAZ"=>30}
114
+
115
+ h.get("Bar")
116
+ # => Some(2)
117
+
118
+ h.get("Foo")
119
+ # => None
120
+ ```
121
+
122
+ ## Approach
123
+
124
+ There have been [many](http://moonbase.rydia.net/mental/writings/programming/monads-in-ruby/00introduction.html)
125
+ [posts](http://pretheory.wordpress.com/2008/02/14/the-maybe-monad-in-ruby/)
126
+ [and](http://www.valuedlessons.com/2008/01/monads-in-ruby-with-nice-syntax.html)
127
+ [discussions](http://stackoverflow.com/questions/2709361/monad-equivalent-in-ruby)
128
+ about monads in Ruby, which have sparked a number of approaches.
129
+
130
+ Rumonade wants to be a practical drop-in Monad solution that will fit well into the Ruby world.
131
+
132
+ The priorities for Rumonade are:
133
+
134
+ 1. Practical usability in day-to-day Ruby
135
+ * <b>don't</b> mess up normal idioms of the language (e.g., `Hash#map`)
136
+ * <b>don't</b> slow down normal idioms of the language (e.g., `Array#map`)
137
+ 2. Rubyish-ness of usage
138
+ * Monad is a mix-in, requiring methods `self.unit` and `#bind` be implemented by target class
139
+ * Prefer blocks to lambda/Procs where possible, but allow both
140
+ 3. Equivalent idioms to Scala where possible
141
+
142
+ ## Status
143
+
144
+ Option, Either, Array, and Hash are already usable.
145
+
146
+ <b><em>Supported Ruby versions</em></b>: MRI 1.9.2, MRI 1.9.3, JRuby in 1.9 mode, and Rubinius in 1.9 mode.
147
+
148
+ Please try it out, and let me know what you think! Suggestions are always welcome.
@@ -18,7 +18,10 @@ module Rumonade
18
18
  METHODS_TO_REPLACE_WITH_MONAD = Monad::DEFAULT_METHODS_TO_REPLACE_WITH_MONAD - [:map]
19
19
 
20
20
  def bind(lam = nil, &blk)
21
- inject(self.class.empty) { |arr, elt| arr + (lam || blk).call(elt).to_a }
21
+ inject(self.class.empty) do |arr, elt|
22
+ res = (lam || blk).call(elt)
23
+ arr + (res.respond_to?(:to_a) ? res.to_a : [res])
24
+ end
22
25
  end
23
26
  end
24
27
  end
@@ -319,4 +319,7 @@ module Rumonade
319
319
  end
320
320
  end
321
321
  end
322
+
323
+ module_function :Left, :Right
324
+ public :Left, :Right
322
325
  end
@@ -55,8 +55,12 @@ module Rumonade
55
55
  # [Some(Some(1)), Some(Some(None))], [None]].flatten
56
56
  # #=> [1]
57
57
  #
58
- def flatten_with_monad
59
- bind { |x| x.is_a?(Monad) ? x.flatten_with_monad : self.class.unit(x) }
58
+ def flatten_with_monad(depth=nil)
59
+ if depth.is_a? Integer
60
+ depth.times.inject(self) {|e, _| e.shallow_flatten }
61
+ else
62
+ bind { |x| x.is_a?(Monad) ? x.flatten_with_monad : self.class.unit(x) }
63
+ end
60
64
  end
61
65
 
62
66
  # Returns a monad whose elements are all those elements of this monad for which the given predicate returned true
@@ -71,11 +75,9 @@ module Rumonade
71
75
  # with the native Ruby flatten calls (multiple-level flattening).
72
76
  #
73
77
  # @example
74
- # [Some(Some(1)), Some(Some(None))], [None]].shallow_flatten
75
- # #=> [Some(Some(1)), Some(Some(None)), None]
76
- # [Some(Some(1)), Some(Some(None)), None].shallow_flatten
77
- # #=> [Some(1), Some(None)]
78
- # [Some(1), Some(None)].shallow_flatten
78
+ # [Some(Some(1)), Some(Some(None)), [None]].shallow_flatten
79
+ # #=> [Some(1), Some(None), None]
80
+ # [Some(1), Some(None), None].shallow_flatten
79
81
  # #=> [1, None]
80
82
  # [1, None].shallow_flatten
81
83
  # #=> [1]
@@ -1,3 +1,3 @@
1
1
  module Rumonade
2
- VERSION = "0.4.0"
2
+ VERSION = "0.4.1"
3
3
  end
data/test/array_test.rb CHANGED
@@ -24,6 +24,7 @@ class ArrayTest < Test::Unit::TestCase
24
24
 
25
25
  def test_flat_map_behaves_correctly
26
26
  assert_equal ["FOO", "BAR"], ["foo", "bar"].flat_map { |s| [s.upcase] }
27
+ assert_equal [2, 4, 6], [1, 2, 3].flat_map { |i| i * 2 }
27
28
  end
28
29
 
29
30
  def test_map_behaves_correctly
@@ -35,6 +36,7 @@ class ArrayTest < Test::Unit::TestCase
35
36
  assert_equal [1], [None, Some(1)].shallow_flatten
36
37
  assert_equal [1, Some(2)], [None, Some(1), Some(Some(2))].shallow_flatten
37
38
  assert_equal [Some(Some(None))], [Some(Some(Some(None)))].shallow_flatten
39
+ assert_equal [Some(1), Some(None), None], [Some(Some(1)), Some(Some(None)), [None]].shallow_flatten
38
40
  end
39
41
 
40
42
  def test_flatten_behaves_correctly
@@ -42,4 +44,15 @@ class ArrayTest < Test::Unit::TestCase
42
44
  assert_equal [1, 2], [None, Some(1), Some(Some(2))].flatten
43
45
  assert_equal [], [Some(Some(Some(None)))].flatten
44
46
  end
47
+
48
+ def test_flatten_with_argument_behaves_correctly
49
+ assert_equal [0, 1, [2], [[3]], [[[4]]]], [0, [1], [[2]], [[[3]]], [[[[4]]]]].flatten(1)
50
+ assert_equal [0, 1, 2, [3], [[4]]], [0, [1], [[2]], [[[3]]], [[[[4]]]]].flatten(2)
51
+ assert_equal [0, 1, 2, 3, [4]], [0, [1], [[2]], [[[3]]], [[[[4]]]]].flatten(3)
52
+ assert_equal [0, 1, 2, 3, 4], [0, [1], [[2]], [[[3]]], [[[[4]]]]].flatten(4)
53
+ assert_equal [Some(Some(1)), Some(Some(None)), [None]], [Some(Some(1)), Some(Some(None)), [None]].flatten(0)
54
+ assert_equal [Some(1), Some(None), None], [Some(Some(1)), Some(Some(None)), [None]].flatten(1)
55
+ assert_equal [1, None], [Some(Some(1)), Some(Some(None)), [None]].flatten(2)
56
+ assert_equal [1], [Some(Some(1)), Some(Some(None)), [None]].flatten(3)
57
+ end
45
58
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rumonade
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-12 00:00:00.000000000 Z
12
+ date: 2013-05-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: test-unit
@@ -52,11 +52,12 @@ extensions: []
52
52
  extra_rdoc_files: []
53
53
  files:
54
54
  - .gitignore
55
+ - .rvmrc
55
56
  - .travis.yml
56
57
  - Gemfile
57
58
  - HISTORY.md
58
59
  - MIT-LICENSE.txt
59
- - README.rdoc
60
+ - README.md
60
61
  - Rakefile
61
62
  - lib/rumonade.rb
62
63
  - lib/rumonade/array.rb
@@ -96,7 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
96
97
  version: '0'
97
98
  requirements: []
98
99
  rubyforge_project: rumonade
99
- rubygems_version: 1.8.24
100
+ rubygems_version: 1.8.25
100
101
  signing_key:
101
102
  specification_version: 3
102
103
  summary: A Scala-inspired Monad library for Ruby
data/README.rdoc DELETED
@@ -1,133 +0,0 @@
1
- {<img src="https://secure.travis-ci.org/ms-ati/rumonade.png?branch=master" alt="Build Status" />}[http://travis-ci.org/ms-ati/rumonade]
2
-
3
- = Rumonade[https://rubygems.org/gems/rumonade]
4
-
5
- *_Project_*: github[http://github.com/ms-ati/rumonade]
6
-
7
- *_Documentation_*: rubydoc.info[http://rubydoc.info/gems/rumonade/frames]
8
-
9
- == A Ruby[http://www.ruby-lang.org] Monad[http://en.wikipedia.org/wiki/Monad_(functional_programming)] Library, Inspired by Scala[http://www.scala-lang.org]
10
-
11
- Are you working in both the Scala[http://www.scala-lang.org] and Ruby[http://www.ruby-lang.org] worlds,
12
- and finding that you miss some of the practical benefits of Scala's
13
- monads[http://james-iry.blogspot.com/2007/09/monads-are-elephants-part-1.html] in Ruby?
14
- Then Rumonade is for you.
15
-
16
- The goal of this library is to make the most common and useful Scala monadic idioms available in Ruby via the following classes:
17
- * {Rumonade::Option Option}
18
- * {Rumonade::ArrayExtensions Array}
19
- * {Rumonade::Either Either}
20
- * {Rumonade::Hash Hash}
21
- * (_more_ _TBD_)
22
-
23
- Syntactic support for scala-like for-comprehensions[http://www.scala-lang.org/node/111] will be implemented
24
- as a sequence of calls to #flat_map, #select, etc, modeling Scala's
25
- approach[http://stackoverflow.com/questions/3754089/scala-for-comprehension/3754568#3754568].
26
-
27
- Support for an all_catch[http://www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/library/scala/util/control/Exception$.html]
28
- idiom will be implemented to turn blocks which might throw exceptions into Option or Either
29
- results. If this proves useful (and a good fit for Ruby), then more narrow functional catchers can be implemented as well.
30
-
31
- == Usage Examples
32
-
33
- === {Rumonade::Option Option}: handle _possibly_ _nil_ values in a _functional_ fashion:
34
-
35
- def format_date_in_march(time_or_date_or_nil)
36
- Option(time_or_date_or_nil). # wraps possibly-nil value in an Option monad (Some or None)
37
- map(&:to_date). # transforms a contained Time value into a Date value
38
- select {|d| d.month == 3}. # filters out non-matching Date values (Some becomes None)
39
- map(&:to_s). # transforms a contained Date value into a String value
40
- map {|s| s.gsub('-', '')}. # transforms a contained String value by removing '-'
41
- get_or_else("not in march!") # returns the contained value, or the alternative if None
42
- end
43
-
44
- format_date_in_march(nil) # => "not in march!"
45
- format_date_in_march(Time.parse('2009-01-01 01:02')) # => "not in march!"
46
- format_date_in_march(Time.parse('2011-03-21 12:34')) # => "20110321"
47
-
48
- Note:
49
- * each step of the chained computations above are functionally isolated
50
- * the value can notionally _start_ as nil, or _become_ nil during a computation, without effecting any other chained computations
51
-
52
- ---
53
- === {Rumonade::Either Either}: handle failures ({Rumonade::Left Left}) and successes ({Rumonade::Right Right}) in a _functional_ fashion:
54
-
55
- def find_person(name)
56
- case name
57
- when /Jack/i, /John/i
58
- Right(name.capitalize)
59
- else
60
- Left("No such person: #{name.capitalize}")
61
- end
62
- end
63
-
64
- # success looks like this:
65
- find_person("Jack")
66
- # => Right("Jack")
67
-
68
- # failure looks like this:
69
- find_person("Jill")
70
- # => Left("No such person: Jill")
71
-
72
- # lift the contained values into Array, in order to combine them:
73
- find_person("Joan").lift_to_a
74
- # => Left(["No such person: Joan"])
75
-
76
- # on the 'happy path', combine and transform successes into a single success result:
77
- (find_person("Jack").lift_to_a +
78
- find_person("John").lift_to_a).right.map { |*names| names.join(" and ") }
79
- # => Right("Jack and John")
80
-
81
- # but if there were errors, we still have a Left with all the errors inside:
82
- (find_person("Jack").lift_to_a +
83
- find_person("John").lift_to_a +
84
- find_person("Jill").lift_to_a +
85
- find_person("Joan").lift_to_a).right.map { |*names| names.join(" and ") }
86
- # => Left(["No such person: Jill", "No such person: Joan"])
87
-
88
- # equivalent to the previous example, but shorter:
89
- %w(Jack John Jill Joan).map { |nm| find_person(nm).lift_to_a }.inject(:+).
90
- right.map { |*names| names.join(" and ") }
91
- # => Left(["No such person: Jill", "No such person: Joan"])
92
-
93
- ---
94
- === {Rumonade::Hash Hash}:
95
-
96
- h = { "Foo" => 1, "Bar" => 2, "Baz" => 3 }
97
- h = h.flat_map { |k, v| { k => v, k.upcase => v * 10 } }
98
- # => {"Foo"=>1, "FOO"=>10, "Bar"=>2, "BAR"=>20, "Baz"=>3, "BAZ"=>30}
99
- h = h.select { |k, v| k =~ /^b/i }
100
- # => {"Bar"=>2, "BAR"=>20, "Baz"=>3, "BAZ"=>30}
101
- h.get("Bar")
102
- # => Some(2)
103
- h.get("Foo")
104
- # => None
105
-
106
- (_more_ _examples_ _coming_ _soon_...)
107
-
108
- == Approach
109
-
110
- There have been many[http://moonbase.rydia.net/mental/writings/programming/monads-in-ruby/00introduction.html]
111
- posts[http://pretheory.wordpress.com/2008/02/14/the-maybe-monad-in-ruby/]
112
- and[http://www.valuedlessons.com/2008/01/monads-in-ruby-with-nice-syntax.html]
113
- discussions[http://stackoverflow.com/questions/2709361/monad-equivalent-in-ruby]
114
- about monads in Ruby, which have sparked a number of approaches.
115
-
116
- Rumonade wants to be a practical drop-in Monad solution that will fit well into the Ruby world.
117
-
118
- The priorities for Rumonade are:
119
- 1. Practical usability in day-to-day Ruby
120
- * <b>don't</b> mess up normal idioms of the language (e.g., Hash#map)
121
- * <b>don't</b> slow down normal idioms of the language (e.g., Array#map)
122
- 2. Rubyish-ness of usage
123
- * Monad is a mix-in, requiring methods +self.unit+ and +#bind+ be implemented by target classes
124
- * Prefer blocks to lambda/Procs where possible, but allow both
125
- 3. Equivalent idioms to Scala where possible
126
-
127
- == Status
128
-
129
- Option, Either, Array, and Hash are already usable.
130
-
131
- <b><em>Supported Ruby versions</em></b>: MRI 1.9.2, MRI 1.9.3, JRuby in 1.9 mode, and Rubinius in 1.9 mode.
132
-
133
- Please try it out, and let me know what you think! Suggestions are always welcome.