functional-ruby 1.1.0 → 1.2.0

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/README.md +14 -12
  4. data/doc/memo.md +192 -0
  5. data/doc/pattern_matching.md +481 -0
  6. data/doc/protocol.md +219 -0
  7. data/doc/record.md +247 -0
  8. data/lib/functional/abstract_struct.rb +8 -8
  9. data/lib/functional/delay.rb +31 -38
  10. data/lib/functional/either.rb +48 -45
  11. data/lib/functional/final_struct.rb +23 -34
  12. data/lib/functional/final_var.rb +20 -21
  13. data/lib/functional/memo.rb +33 -24
  14. data/lib/functional/method_signature.rb +1 -2
  15. data/lib/functional/option.rb +7 -7
  16. data/lib/functional/pattern_matching.rb +12 -10
  17. data/lib/functional/protocol.rb +2 -4
  18. data/lib/functional/protocol_info.rb +5 -3
  19. data/lib/functional/record.rb +82 -16
  20. data/lib/functional/synchronization.rb +88 -0
  21. data/lib/functional/tuple.rb +14 -4
  22. data/lib/functional/type_check.rb +0 -2
  23. data/lib/functional/union.rb +5 -4
  24. data/lib/functional/value_struct.rb +5 -3
  25. data/lib/functional/version.rb +1 -1
  26. data/spec/functional/complex_pattern_matching_spec.rb +1 -2
  27. data/spec/functional/configuration_spec.rb +0 -2
  28. data/spec/functional/delay_spec.rb +0 -2
  29. data/spec/functional/either_spec.rb +0 -1
  30. data/spec/functional/final_struct_spec.rb +0 -1
  31. data/spec/functional/final_var_spec.rb +0 -2
  32. data/spec/functional/memo_spec.rb +7 -10
  33. data/spec/functional/option_spec.rb +0 -1
  34. data/spec/functional/pattern_matching_spec.rb +0 -1
  35. data/spec/functional/protocol_info_spec.rb +0 -2
  36. data/spec/functional/protocol_spec.rb +1 -3
  37. data/spec/functional/record_spec.rb +170 -87
  38. data/spec/functional/tuple_spec.rb +0 -1
  39. data/spec/functional/type_check_spec.rb +0 -2
  40. data/spec/functional/union_spec.rb +0 -1
  41. data/spec/functional/value_struct_spec.rb +0 -1
  42. metadata +14 -29
  43. data/doc/memo.txt +0 -192
  44. data/doc/pattern_matching.txt +0 -485
  45. data/doc/protocol.txt +0 -221
  46. data/doc/record.txt +0 -207
  47. data/doc/thread_safety.txt +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c56d810dbee5d5fb2e4b26daef765a75700d3e4f
4
- data.tar.gz: e3e27de5c017c07eef5c9d78c590e73251fa98a9
3
+ metadata.gz: 45800b8ffb755a47bf66e40dc7a7832d2577f174
4
+ data.tar.gz: 0becaff048134f9616f871e6ae2f1b42a50fea66
5
5
  SHA512:
6
- metadata.gz: 84ba63762cb021ceb483e8f175a23c9ea11c12b5fa66764662670b9a688d4d2a0f15bb7baed3b8e2b2207a5b37613b62dc1c90c647bbb68176c0208b41c7cf8e
7
- data.tar.gz: 7411ea271610c82bbf180e28797ab0e0a0795200d7c77f252e435e098acfe7224236fd59700b72fad726d4a03d43fd1829277f75b14dd8f7f8afd615a44410e0
6
+ metadata.gz: 68918e9ef2c30806b416512d687bdf4c4d9fb27ef3c6d869cd78f43435e1411de2b443f29c760dd67cc39a2a0e5cf639502054b0924c5413246955b360f567ee
7
+ data.tar.gz: 7765dd90d059280a7018a12103b2a3961636fd31325c7ee3ffd2c22944bfca0181226596794dc8cb7850506e7075cb28541992b17a8209bcbcc6540296cbdf9b
@@ -1,3 +1,7 @@
1
+ ### Upcoming Release v1.2.0 (TBD)
2
+
3
+ * `Record` classes can be declared with a type/protocol specification for type safety.
4
+
1
5
  ## Current Release v1.1.0 (August 12, 2014)
2
6
 
3
7
  * A simple implementation of [tuple](http://en.wikipedia.org/wiki/Tuple), an
data/README.md CHANGED
@@ -1,15 +1,16 @@
1
1
  # Functional Ruby
2
- [![Gem Version](https://badge.fury.io/rb/functional-ruby.svg)](http://badge.fury.io/rb/functional-ruby) [![Build Status](https://secure.travis-ci.org/jdantonio/functional-ruby.png)](https://travis-ci.org/jdantonio/functional-ruby?branch=master) [![Coverage Status](https://coveralls.io/repos/jdantonio/functional-ruby/badge.png)](https://coveralls.io/r/jdantonio/functional-ruby) [![Code Climate](https://codeclimate.com/github/jdantonio/functional-ruby.png)](https://codeclimate.com/github/jdantonio/functional-ruby) [![Inline docs](http://inch-ci.org/github/jdantonio/functional-ruby.png)](http://inch-ci.org/github/jdantonio/functional-ruby) [![Dependency Status](https://gemnasium.com/jdantonio/functional-ruby.png)](https://gemnasium.com/jdantonio/functional-ruby) [![License](http://img.shields.io/license/MIT.png?color=green)](http://opensource.org/licenses/MIT)
2
+ [![Gem Version](https://badge.fury.io/rb/functional-ruby.svg)](http://badge.fury.io/rb/functional-ruby)
3
+ [![Travis CI Build Status](https://secure.travis-ci.org/jdantonio/functional-ruby.png)](https://travis-ci.org/jdantonio/functional-ruby?branch=master)
4
+ [![AppVeyor Build status](https://ci.appveyor.com/api/projects/status/8xfy4a8lmc26112e/branch/master?svg=true)](https://ci.appveyor.com/project/jdantonio/functional-ruby/branch/master)
5
+ [![Coverage Status](https://coveralls.io/repos/jdantonio/functional-ruby/badge.png)](https://coveralls.io/r/jdantonio/functional-ruby)
6
+ [![Code Climate](https://codeclimate.com/github/jdantonio/functional-ruby.png)](https://codeclimate.com/github/jdantonio/functional-ruby)
7
+ [![Inline docs](http://inch-ci.org/github/jdantonio/functional-ruby.png)](http://inch-ci.org/github/jdantonio/functional-ruby)
8
+ [![Dependency Status](https://gemnasium.com/jdantonio/functional-ruby.png)](https://gemnasium.com/jdantonio/functional-ruby)
9
+ [![License](http://img.shields.io/license/MIT.png?color=green)](http://opensource.org/licenses/MIT)
3
10
 
4
11
  **A gem for adding functional programming tools to Ruby. Inspired by [Erlang](http://www.erlang.org/),
5
12
  [Clojure](http://clojure.org/), and [Functional Java](http://functionaljava.org/).**
6
13
 
7
- *NOTE: Version 1.0 is a complete rewrite. Previous versions lacked a unified
8
- focus. Version 1.0 is a cohesive set of utilities inspired by other languages
9
- but designed to work together in ways idiomatic to Ruby.*
10
-
11
- Please see the [changelog](https://github.com/jdantonio/functional-ruby/blob/master/CHANGELOG.md) for more information.
12
-
13
14
  ## Introduction
14
15
 
15
16
  Two things I love are [Ruby](http://www.ruby-lang.org/en/) and
@@ -36,7 +37,7 @@ Our goal is to implement various functional programming patterns in Ruby. Specif
36
37
 
37
38
  ## Features
38
39
 
39
- Complete API documentation can be found at [Rubydoc.info](http://rubydoc.info/github/jdantonio/functional-ruby/master/frames).
40
+ The primary site for documentation is the automatically generated [API documentation](http://jdantonio.github.io/functional-ruby/).
40
41
 
41
42
  * Protocol specifications inspired by Clojure [protocol](http://clojure.org/protocols),
42
43
  Erlang [behavior](http://www.erlang.org/doc/design_principles/des_princ.html#id60128),
@@ -49,6 +50,7 @@ Complete API documentation can be found at [Rubydoc.info](http://rubydoc.info/gi
49
50
  * Thread safe, immutable `Either` and `Option` classes based on [Functional Java](http://functionaljava.org/) and [Haskell](https://hackage.haskell.org/package/base-4.2.0.1/docs/Data-Either.html).
50
51
  * [Memoization](http://en.wikipedia.org/wiki/Memoization) of class methods based on Clojure [memoize](http://clojuredocs.org/clojure_core/clojure.core/memoize).
51
52
  * Lazy execution with a `Delay` class based on Clojure [delay](http://clojuredocs.org/clojure_core/clojure.core/delay).
53
+ * `ValueStruct`, a simple, thread safe, immutable variation of Ruby's [OpenStruct](http://ruby-doc.org/stdlib-2.0/libdoc/ostruct/rdoc/OpenStruct.html) class.
52
54
  * Thread safe data structures, such as `FinalStruct` and `FinalVar`, which can be written to at most once
53
55
  before becoming immutable. Based on [Java's `final` keyword](http://en.wikipedia.org/wiki/Final_(Java)).
54
56
 
@@ -116,7 +118,7 @@ and other options:
116
118
  class Foo
117
119
  include Functional::PatternMatching
118
120
  include Functional::Protocol
119
- include Functional::TypeChecking
121
+ include Functional::TypeCheck
120
122
 
121
123
  def greet
122
124
  return 'Hello, World!'
@@ -127,12 +129,12 @@ class Foo
127
129
  end
128
130
 
129
131
  defn(:greet, _) { |name|
130
- "Pleased to meet you, #{name.fulle_name}!"
131
- }.when {|name| Type?(CustomerModel, ClientModel) }
132
+ "Pleased to meet you, #{name.full_name}!"
133
+ }.when {|name| Type?(name, CustomerModel, ClientModel) }
132
134
 
133
135
  defn(:greet, _) { |name|
134
136
  "Hello, #{name.first} #{name.last}!"
135
- }.when {|name| Satisfy?(:Name) }
137
+ }.when {|name| Satisfy?(name, :Name) }
136
138
 
137
139
  defn(:greet, :doctor, _) { |name|
138
140
  "Hello, Dr. #{name}!"
@@ -0,0 +1,192 @@
1
+ # memoize
2
+
3
+ ### Rationale
4
+
5
+ Many computational operations take a significant amount of time and/or use
6
+ an inordinate amount of resources. If subsequent calls to that function with
7
+ the same parameters are guaranteed to return the same result, caching the
8
+ result can lead to significant performance improvements. The process of
9
+ caching such calls is called
10
+ [memoization](http://en.wikipedia.org/wiki/Memoization).
11
+
12
+ ### Declaration
13
+
14
+ Using memoization requires two simple steps: including the
15
+ `Functional::Memo` module within a class or module and calling the `memoize`
16
+ function to enable memoization on one or more methods.
17
+
18
+ ```ruby
19
+ Module EvenNumbers
20
+ include Functional::Memoize
21
+
22
+ self.first(n)
23
+ (2..n).select{|i| i % 2 == 0 }
24
+ end
25
+
26
+ memoize :first
27
+ end
28
+ ```
29
+
30
+ When a function is memoized an internal cache is created that maps arguments
31
+ to return values. When the function is called the arguments are checked
32
+ against the cache. If the args are found the method is not called and the
33
+ cached result is returned instead.
34
+
35
+ ### Ramifications
36
+
37
+ Memoizing long-running methods can lead to significant performance
38
+ advantages. But there is a trade-off. Memoization may greatly increase the
39
+ memory footprint of the application. The memo cache itself takes memory. The
40
+ more arg/result pairs stored in the cache, the more memory is consumed.
41
+
42
+ ##### Cache Size Options
43
+
44
+ To help control the size of the cache, a limit can be placed on the number
45
+ of items retained in the cache. The `:at_most` option, when given, indicates
46
+ the maximum size of the cache. Once the maximum cache size is reached, calls
47
+ to to the method with uncached args will still result in the method being
48
+ called, but the results will not be cached.
49
+
50
+ ```ruby
51
+ Module EvenNumbers
52
+ include Functional::Memoize
53
+
54
+ self.first(n)
55
+ (2..n).select{|i| i % 2 == 0 }
56
+ end
57
+
58
+ memoize :first, at_most: 1000
59
+ end
60
+ ```
61
+
62
+ There is no way to predict in advance what the proper cache size is, or if
63
+ it should be restricted at all. Only performance testing under realistic
64
+ conditions or profiling of a running system can provide guidance.
65
+
66
+ ### Restrictions
67
+
68
+ Not all methods are good candidates for memoization.Only methods that are
69
+ [idempotent](http://en.wikipedia.org/wiki/Idempotence), [referentially
70
+ transparent](http://en.wikipedia.org/wiki/Referential_transparency_(computer_science)),
71
+ and free of [side effects](http://en.wikipedia.org/wiki/Side_effect_(computer_science))
72
+ can be effectively memoized. If a method creates side effects, such as
73
+ writing to a log, only the first call to the method will create those side
74
+ effects. Subsequent calls will return the cached value without calling the
75
+ method.
76
+
77
+ Similarly, methods which change internal state will only update the state on
78
+ the initial call. Later calls will not result in state changes, they will
79
+ only return the original result. Subsequently, instance methods cannot be
80
+ memoized. Objects are, by definition, stateful. Method calls exist for the
81
+ purpose of changing or using the internal state of the object. Such methods
82
+ cannot be effectively memoized; it would require the internal state of the
83
+ object to be cached and checked as well.
84
+
85
+ Block parameters pose a similar problem. Block parameters are inherently
86
+ stateful (they are closures which capture the enclosing context). And there
87
+ is no way to check the state of the block along with the args to determine
88
+ if the cached value should be used. Subsequently, and method call which
89
+ includes a block will result in the cache being completely skipped. The base
90
+ method will be called and the result will not be cached. This behavior will
91
+ occur even when the given method was not programmed to accept a block
92
+ parameter. Ruby will capture any block passed to any method and make it
93
+ available to the method even when not documented as a formal parameter or
94
+ used in the method. This has the interesting side effect of allowing the
95
+ memo cache to be skipped on any method call, simply be passing a block
96
+ parameter.
97
+
98
+ ```ruby
99
+ EvenNumbers.first(100) causes the result to be cached
100
+ EvenNumbers.first(100) retrieves the previous result from the cache
101
+ EvenNumbers.first(100){ nil } skips the memo cache and calls the method again
102
+ ```
103
+
104
+ ### Complete Example
105
+
106
+ The following example is borrowed from the book [Functional Thinking](http://shop.oreilly.com/product/0636920029687.do)
107
+ by Neal Ford. In his book he shows an example of memoization in Groovy by
108
+ summing factors of a given number. This is a great example because it
109
+ exhibits all the criteria that make a method a good memoization candidate:
110
+
111
+ * Idempotence
112
+ * Referential transparency
113
+ * Stateless
114
+ * Free of side effect
115
+ * Computationally expensive (for large numbers)
116
+
117
+ The following code implements Ford's algorithms in Ruby, then memoizes two
118
+ key methods. The Ruby code:
119
+
120
+ ```ruby
121
+ require 'functional'
122
+
123
+ class Factors
124
+ include Functional::Memo
125
+
126
+ def self.sum_of(number)
127
+ of(number).reduce(:+)
128
+ end
129
+
130
+ def self.of(number)
131
+ (1..number).select {|i| factor?(number, i)}
132
+ end
133
+
134
+ def self.factor?(number, potential)
135
+ number % potential == 0
136
+ end
137
+
138
+ def self.perfect?(number)
139
+ sum_of(number) == 2 * number
140
+ end
141
+
142
+ def self.abundant?(number)
143
+ sum_of(number) > 2 * number
144
+ end
145
+
146
+ def self.deficient?(number)
147
+ sum_of(number) < 2 * number
148
+ end
149
+
150
+ memoize(:sum_of)
151
+ memoize(:of)
152
+ end
153
+ ```
154
+
155
+ This code was tested in IRB using MRI 2.1.2 on a MacBook Pro. The `sum_of`
156
+ method was called three times against the number 10,000,000 and the
157
+ benchmark results of each run were captured. The test code:
158
+
159
+ ```ruby
160
+ require 'benchmark'
161
+
162
+ 3.times do
163
+ stats = Benchmark.measure do
164
+ Factors.sum_of(10_000_000)
165
+ end
166
+ puts stats
167
+ end
168
+ ```
169
+
170
+ The results of the benchmarking are very revealing. The first run took over
171
+ a second to calculate the results. The two subsequent runs, which retrieved
172
+ the previous result from the memo cache, were nearly instantaneous:
173
+
174
+ ```
175
+ 1.080000 0.000000 1.080000 ( 1.077524)
176
+ 0.000000 0.000000 0.000000 ( 0.000033)
177
+ 0.000000 0.000000 0.000000 ( 0.000008)
178
+ ```
179
+
180
+ The same code run on the same computer using JRuby 1.7.12 exhibited similar
181
+ results:
182
+
183
+ ```
184
+ 1.800000 0.030000 1.830000 ( 1.494000)
185
+ 0.000000 0.000000 0.000000 ( 0.000000)
186
+ 0.000000 0.000000 0.000000 ( 0.000000)
187
+ ```
188
+
189
+ ### Inspiration
190
+
191
+ * [Memoization](http://en.wikipedia.org/wiki/Memoization) at Wikipedia
192
+ * Clojure [memoize](http://clojuredocs.org/clojure_core/clojure.core/memoize) function
@@ -0,0 +1,481 @@
1
+ ### Features
2
+
3
+ * Pattern matching for instance methods.
4
+ * Pattern matching for object constructors.
5
+ * Parameter count matching
6
+ * Matching against primitive values
7
+ * Matching by class/datatype
8
+ * Matching against specific key/vaue pairs in hashes
9
+ * Matching against the presence of keys within hashes
10
+ * Implicit hash for last parameter
11
+ * Variable-length parameter lists
12
+ * Guard clauses
13
+ * Recursive calls to other pattern matches
14
+ * Recursive calls to superclass pattern matches
15
+ * Recursive calls to superclass methods
16
+ * Dispatching to superclass methods when no match is found
17
+ * Reasonable error messages when no match is found
18
+
19
+ ### Usage
20
+
21
+ First, familiarize yourself with Erlang [pattern matching](http://learnyousomeerlang.com/syntax-in-functionspattern-matching).
22
+ This gem may not make much sense if you don't understand how Erlang dispatches functions.
23
+
24
+ In the Ruby class file where you want to use pattern matching, require the *functional-ruby* gem:
25
+
26
+ ```ruby
27
+ require 'functional'
28
+ ```
29
+
30
+ Then include `Functional::PatternMatching` in your class:
31
+
32
+ ```ruby
33
+ require 'functional'
34
+
35
+ class Foo
36
+ include Functional::PatternMatching
37
+
38
+ ...
39
+
40
+ end
41
+ ```
42
+
43
+ You can then define functions with `defn` instead of the normal *def* statement.
44
+ The syntax for `defn` is:
45
+
46
+ ```ruby
47
+ defn(:symbol_name_of_function, zero, or, more, parameters) { |block, arguments|
48
+ code to execute
49
+ }
50
+ ```
51
+ You can then call your new function just like any other:
52
+
53
+ ```ruby
54
+ require 'functional/pattern_matching'
55
+
56
+ class Foo
57
+ include Functional::PatternMatching
58
+
59
+ defn(:hello) {
60
+ puts "Hello, World!"
61
+ }
62
+ end
63
+
64
+ foo = Foo.new
65
+ foo.hello => "Hello, World!"
66
+ ```
67
+
68
+ Patterns to match against are included in the parameter list:
69
+
70
+ ```ruby
71
+ defn(:greet, :male) {
72
+ puts "Hello, sir!"
73
+ }
74
+
75
+ defn(:greet, :female) {
76
+ puts "Hello, ma'am!"
77
+ }
78
+
79
+ ...
80
+
81
+ foo.greet(:male) => "Hello, sir!"
82
+ foo.greet(:female) => "Hello, ma'am!"
83
+ ```
84
+
85
+ If a particular method call can not be matched a *NoMethodError* is thrown with
86
+ a reasonably helpful error message:
87
+
88
+ ```ruby
89
+ foo.greet(:unknown) => NoMethodError: no method `greet` matching [:unknown] found for class Foo
90
+ foo.greet => NoMethodError: no method `greet` matching [] found for class Foo
91
+ ```
92
+
93
+ Parameters that are expected to exist but that can take any value are considered
94
+ *unbound* parameters. Unbound parameters are specified by the `_` underscore
95
+ character or `UNBOUND`:
96
+
97
+ ```ruby
98
+ defn(:greet, _) do |name|
99
+ "Hello, {name}!"
100
+ end
101
+
102
+ defn(:greet, UNBOUND, UNBOUND) do |first, last|
103
+ "Hello, {first} {last}!"
104
+ end
105
+
106
+ ...
107
+
108
+ foo.greet('Jerry') => "Hello, Jerry!"
109
+ ```
110
+
111
+ All unbound parameters will be passed to the block in the order they are specified in the definition:
112
+
113
+ ```ruby
114
+ defn(:greet, _, _) do |first, last|
115
+ "Hello, {first} {last}!"
116
+ end
117
+
118
+ ...
119
+
120
+ foo.greet('Jerry', "D'Antonio") => "Hello, Jerry D'Antonio!"
121
+ ```
122
+
123
+ If for some reason you don't care about one or more unbound parameters within
124
+ the block you can use the `_` underscore character in the block parameters list
125
+ as well:
126
+
127
+ ```ruby
128
+ defn(:greet, _, _, _) do |first, _, last|
129
+ "Hello, {first} {last}!"
130
+ end
131
+
132
+ ...
133
+
134
+ foo.greet('Jerry', "I'm not going to tell you my middle name!", "D'Antonio") => "Hello, Jerry D'Antonio!"
135
+ ```
136
+
137
+ Hash parameters can match against specific keys and either bound or unbound parameters. This allows for
138
+ function dispatch by hash parameters without having to dig through the hash:
139
+
140
+ ```ruby
141
+ defn(:hashable, {foo: :bar}) { |opts|
142
+ :foo_bar
143
+ }
144
+ defn(:hashable, {foo: _}) { |f|
145
+ f
146
+ }
147
+
148
+ ...
149
+
150
+ foo.hashable({foo: :bar}) => :foo_bar
151
+ foo.hashable({foo: :baz}) => :baz
152
+ ```
153
+
154
+ The Ruby idiom of the final parameter being a hash is also supported:
155
+
156
+ ```ruby
157
+ defn(:options, _) { |opts|
158
+ opts
159
+ }
160
+
161
+ ...
162
+
163
+ foo.options(bar: :baz, one: 1, many: 2)
164
+ ```
165
+
166
+ As is the Ruby idiom of variable-length argument lists. The constant `ALL` as the last parameter
167
+ will match one or more arguments and pass them to the block as an array:
168
+
169
+ ```ruby
170
+ defn(:baz, Integer, ALL) { |int, args|
171
+ [int, args]
172
+ }
173
+ defn(:baz, ALL) { |args|
174
+ args
175
+ }
176
+ ```
177
+
178
+ Superclass polymorphism is supported as well. If an object cannot match a method
179
+ signature it will defer to the parent class:
180
+
181
+ ```ruby
182
+ class Bar
183
+ def greet
184
+ return 'Hello, World!'
185
+ end
186
+ end
187
+
188
+ class Foo < Bar
189
+ include Functional::PatternMatching
190
+
191
+ defn(:greet, _) do |name|
192
+ "Hello, {name}!"
193
+ end
194
+ end
195
+
196
+ ...
197
+
198
+ foo.greet('Jerry') => "Hello, Jerry!"
199
+ foo.greet => "Hello, World!"
200
+ ```
201
+
202
+ Guard clauses in Erlang are defined with `when` clauses between the parameter list and the function body.
203
+ In Ruby, guard clauses are defined by chaining a call to `when` onto the the `defn` call and passing
204
+ a block. If the guard clause evaluates to true then the function will match. If the guard evaluates
205
+ to false the function will not match and pattern matching will continue:
206
+
207
+ Erlang:
208
+
209
+ ```erlang
210
+ old_enough(X) when X >= 16 -> true;
211
+ old_enough(_) -> false.
212
+ ```
213
+
214
+ Ruby:
215
+
216
+ ```ruby
217
+ defn(:old_enough, _){ true }.when{|x| x >= 16 }
218
+ defn(:old_enough, _){ false }
219
+ ```
220
+
221
+ ##### Order Matters
222
+
223
+ As with Erlang, the order of pattern matches is significant. Patterns will be matched
224
+ *in the order declared* and the first match will be used. If a particular function call
225
+ can be matched by more than one pattern, the *first matched pattern* will be used. It
226
+ is the programmer's responsibility to ensure patterns are declared in the correct order.
227
+
228
+ ##### Blocks and Procs and Lambdas, oh my!
229
+
230
+ When using this gem it is critical to remember that `defn` takes a block and
231
+ that blocks in Ruby have special rules. There are [plenty](https://www.google.com/search?q=ruby+block+proc+lambda)
232
+ of good tutorials on the web explaining [blocks](http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/)
233
+ and [Procs](https://coderwall.com/p/_-_mha) and [lambdas](http://railsguru.org/2010/03/learn-ruby-procs-blocks-lambda/)
234
+ in Ruby. Please read them. Please don't submit a bug report if you use a
235
+ `return` statement within your `defn` and your code blows up with a
236
+ [LocalJumpError](http://ruby-doc.org/core-2.0/LocalJumpError.html).
237
+
238
+ ##### Examples
239
+
240
+ For more examples see the integration tests in *spec/integration_spec.rb*.
241
+
242
+ Simple Functions
243
+
244
+ This example is based on [Syntax in defnctions: Pattern Matching](http://learnyousomeerlang.com/syntax-in-defnctions) in [Learn You Some Erlang for Great Good!](http://learnyousomeerlang.com/).
245
+
246
+ Erlang:
247
+
248
+ ```erlang
249
+ greet(male, Name) ->
250
+ io:format("Hello, Mr. ~s!", [Name]);
251
+ greet(female, Name) ->
252
+ io:format("Hello, Mrs. ~s!", [Name]);
253
+ greet(_, Name) ->
254
+ io:format("Hello, ~s!", [Name]).
255
+ ```
256
+
257
+ Ruby:
258
+
259
+ ```ruby
260
+ require 'functional/pattern_matching'
261
+
262
+ class Foo
263
+ include Functional::PatternMatching
264
+
265
+ defn(:greet, _) do |name|
266
+ "Hello, {name}!"
267
+ end
268
+
269
+ defn(:greet, :male, _) { |name|
270
+ "Hello, Mr. {name}!"
271
+ }
272
+ defn(:greet, :female, _) { |name|
273
+ "Hello, Ms. {name}!"
274
+ }
275
+ defn(:greet, _, _) { |_, name|
276
+ "Hello, {name}!"
277
+ }
278
+ end
279
+ ```
280
+
281
+ ##### Simple Functions with Overloading
282
+
283
+ This example is based on [Syntax in defnctions: Pattern Matching](http://learnyousomeerlang.com/syntax-in-defnctions) in [Learn You Some Erlang for Great Good!](http://learnyousomeerlang.com/).
284
+
285
+ Erlang:
286
+
287
+ ```erlang
288
+ greet(Name) ->
289
+ io:format("Hello, ~s!", [Name]).
290
+
291
+ greet(male, Name) ->
292
+ io:format("Hello, Mr. ~s!", [Name]);
293
+ greet(female, Name) ->
294
+ io:format("Hello, Mrs. ~s!", [Name]);
295
+ greet(_, Name) ->
296
+ io:format("Hello, ~s!", [Name]).
297
+ ```
298
+
299
+ Ruby:
300
+
301
+ ```ruby
302
+ require 'functional/pattern_matching'
303
+
304
+ class Foo
305
+ include Functional::PatternMatching
306
+
307
+ defn(:greet, _) do |name|
308
+ "Hello, {name}!"
309
+ end
310
+
311
+ defn(:greet, :male, _) { |name|
312
+ "Hello, Mr. {name}!"
313
+ }
314
+ defn(:greet, :female, _) { |name|
315
+ "Hello, Ms. {name}!"
316
+ }
317
+ defn(:greet, nil, _) { |name|
318
+ "Goodbye, {name}!"
319
+ }
320
+ defn(:greet, _, _) { |_, name|
321
+ "Hello, {name}!"
322
+ }
323
+ end
324
+ ```
325
+
326
+ Constructor Overloading
327
+
328
+ ```ruby
329
+ require 'functional/pattern_matching'
330
+
331
+ class Foo
332
+ include Functional::PatternMatching
333
+
334
+ defn(:initialize) { @name = 'baz' }
335
+ defn(:initialize, _) {|name| @name = name.to_s }
336
+ end
337
+ ```
338
+
339
+ Matching by Class/Datatype
340
+
341
+ ```ruby
342
+ require 'functional/pattern_matching'
343
+
344
+ class Foo
345
+ include Functional::PatternMatching
346
+
347
+ defn(:concat, Integer, Integer) { |first, second|
348
+ first + second
349
+ }
350
+ defn(:concat, Integer, String) { |first, second|
351
+ "{first} {second}"
352
+ }
353
+ defn(:concat, String, String) { |first, second|
354
+ first + second
355
+ }
356
+ defn(:concat, Integer, _) { |first, second|
357
+ first + second.to_i
358
+ }
359
+ end
360
+ ```
361
+
362
+ Matching a Hash Parameter
363
+
364
+ ```ruby
365
+ require 'functional/pattern_matching'
366
+
367
+ class Foo
368
+ include Functional::PatternMatching
369
+
370
+ defn(:hashable, {foo: :bar}) { |opts|
371
+ matches any hash with key :foo and value :bar
372
+ :foo_bar
373
+ }
374
+ defn(:hashable, {foo: _, bar: _}) { |f, b|
375
+ matches any hash with keys :foo and :bar
376
+ passes the values associated with those keys to the block
377
+ [f, b]
378
+ }
379
+ defn(:hashable, {foo: _}) { |f|
380
+ matches any hash with key :foo
381
+ passes the value associated with that key to the block
382
+ must appear AFTER the prior match or it will override that one
383
+ f
384
+ }
385
+ defn(:hashable, {}) { ||
386
+ matches an empty hash
387
+ :empty
388
+ }
389
+ defn(:hashable, _) { |opts|
390
+ matches any hash (or any other value)
391
+ opts
392
+ }
393
+ end
394
+
395
+ ...
396
+
397
+ foo.hashable({foo: :bar}) => :foo_bar
398
+ foo.hashable({foo: :baz}) => :baz
399
+ foo.hashable({foo: 1, bar: 2}) => [1, 2]
400
+ foo.hashable({foo: 1, baz: 2}) => 1
401
+ foo.hashable({bar: :baz}) => {bar: :baz}
402
+ foo.hashable({}) => :empty
403
+ ```
404
+
405
+ Variable Length Argument Lists with ALL
406
+
407
+ ```ruby
408
+ defn(:all, :one, ALL) { |args|
409
+ args
410
+ }
411
+ defn(:all, :one, Integer, ALL) { |int, args|
412
+ [int, args]
413
+ }
414
+ defn(:all, 1, _, ALL) { |var, args|
415
+ [var, args]
416
+ }
417
+ defn(:all, ALL) { | args|
418
+ args
419
+ }
420
+
421
+ ...
422
+
423
+ foo.all(:one, 'a', 'bee', :see) => ['a', 'bee', :see]
424
+ foo.all(:one, 1, 'bee', :see) => [1, 'bee', :see]
425
+ foo.all(1, 'a', 'bee', :see) => ['a', ['bee', :see]]
426
+ foo.all('a', 'bee', :see) => ['a', 'bee', :see]
427
+ foo.all() => NoMethodError: no method `all` matching [] found for class Foo
428
+ ```
429
+
430
+ ##### Guard Clauses
431
+
432
+ These examples are based on [Syntax in defnctions: Pattern Matching](http://learnyousomeerlang.com/syntax-in-defnctions)
433
+ in [Learn You Some Erlang for Great Good!](http://learnyousomeerlang.com/).
434
+
435
+ Erlang:
436
+
437
+ ```erlang
438
+ old_enough(X) when X >= 16 -> true;
439
+ old_enough(_) -> false.
440
+
441
+ right_age(X) when X >= 16, X =< 104 ->
442
+ true;
443
+ right_age(_) ->
444
+ false.
445
+
446
+ wrong_age(X) when X < 16; X > 104 ->
447
+ true;
448
+ wrong_age(_) ->
449
+ false.
450
+ ```
451
+
452
+ ```ruby
453
+ defn(:old_enough, _){ true }.when{|x| x >= 16 }
454
+ defn(:old_enough, _){ false }
455
+
456
+ defn(:right_age, _) {
457
+ true
458
+ }.when{|x| x >= 16 && x <= 104 }
459
+
460
+ defn(:right_age, _) {
461
+ false
462
+ }
463
+
464
+ defn(:wrong_age, _) {
465
+ false
466
+ }.when{|x| x < 16 || x > 104 }
467
+
468
+ defn(:wrong_age, _) {
469
+ true
470
+ }
471
+ ```
472
+
473
+ ### Inspiration
474
+
475
+ Pattern matching has its roots in logic programming languages such as
476
+ [Prolog](http://en.wikipedia.org/wiki/Prolog). Pattern matching is a core
477
+ feature of the [Erlang](http://www.erlang.org/) programming language. A few
478
+ helpful resources are:
479
+
480
+ * Erlang [modules](http://erlang.org/doc/reference_manual/modules.html)
481
+ * Erlang [pattern matching](http://erlang.org/doc/reference_manual/patterns.html)