rumonade 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
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.