functional-ruby 1.1.0 → 1.2.0

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