functional-ruby 0.7.7 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +92 -152
  3. data/doc/memo.txt +192 -0
  4. data/doc/pattern_matching.txt +485 -0
  5. data/doc/protocol.txt +221 -0
  6. data/doc/record.txt +144 -0
  7. data/doc/thread_safety.txt +8 -0
  8. data/lib/functional.rb +48 -18
  9. data/lib/functional/abstract_struct.rb +161 -0
  10. data/lib/functional/delay.rb +117 -0
  11. data/lib/functional/either.rb +222 -0
  12. data/lib/functional/memo.rb +93 -0
  13. data/lib/functional/method_signature.rb +72 -0
  14. data/lib/functional/option.rb +209 -0
  15. data/lib/functional/pattern_matching.rb +117 -100
  16. data/lib/functional/protocol.rb +157 -0
  17. data/lib/functional/protocol_info.rb +193 -0
  18. data/lib/functional/record.rb +155 -0
  19. data/lib/functional/type_check.rb +112 -0
  20. data/lib/functional/union.rb +152 -0
  21. data/lib/functional/version.rb +3 -1
  22. data/spec/functional/abstract_struct_shared.rb +154 -0
  23. data/spec/functional/complex_pattern_matching_spec.rb +205 -0
  24. data/spec/functional/configuration_spec.rb +17 -0
  25. data/spec/functional/delay_spec.rb +147 -0
  26. data/spec/functional/either_spec.rb +237 -0
  27. data/spec/functional/memo_spec.rb +207 -0
  28. data/spec/functional/option_spec.rb +292 -0
  29. data/spec/functional/pattern_matching_spec.rb +279 -276
  30. data/spec/functional/protocol_info_spec.rb +444 -0
  31. data/spec/functional/protocol_spec.rb +274 -0
  32. data/spec/functional/record_spec.rb +175 -0
  33. data/spec/functional/type_check_spec.rb +103 -0
  34. data/spec/functional/union_spec.rb +110 -0
  35. data/spec/spec_helper.rb +6 -4
  36. metadata +55 -45
  37. data/lib/functional/behavior.rb +0 -138
  38. data/lib/functional/behaviour.rb +0 -2
  39. data/lib/functional/catalog.rb +0 -487
  40. data/lib/functional/collection.rb +0 -403
  41. data/lib/functional/inflect.rb +0 -127
  42. data/lib/functional/platform.rb +0 -120
  43. data/lib/functional/search.rb +0 -132
  44. data/lib/functional/sort.rb +0 -41
  45. data/lib/functional/utilities.rb +0 -189
  46. data/md/behavior.md +0 -188
  47. data/md/catalog.md +0 -32
  48. data/md/collection.md +0 -32
  49. data/md/inflect.md +0 -32
  50. data/md/pattern_matching.md +0 -512
  51. data/md/platform.md +0 -32
  52. data/md/search.md +0 -32
  53. data/md/sort.md +0 -32
  54. data/md/utilities.md +0 -55
  55. data/spec/functional/behavior_spec.rb +0 -528
  56. data/spec/functional/catalog_spec.rb +0 -1206
  57. data/spec/functional/collection_spec.rb +0 -752
  58. data/spec/functional/inflect_spec.rb +0 -85
  59. data/spec/functional/integration_spec.rb +0 -205
  60. data/spec/functional/platform_spec.rb +0 -501
  61. data/spec/functional/search_spec.rb +0 -187
  62. data/spec/functional/sort_spec.rb +0 -61
  63. data/spec/functional/utilities_spec.rb +0 -277
@@ -1,188 +0,0 @@
1
- # For good -behavior(:timeoff).
2
-
3
- One of Ruby's greatest strengths is [duck typing](http://rubylearning.com/satishtalim/duck_typing.html).
4
- Usually this is awesome and I'm happy to not have to deal with static typing and the compiler. Usually.
5
- The problem with duck typing is that is is impossible in Ruby to enforce an interface definition.
6
- I would never advocate turning Ruby into the cesspool complex object creation that Java has
7
- unfortunately become, but occasionally it would be nice to make sure a class implements a set of
8
- required methods. Enter Erlang's [-behavior](http://metajack.im/2008/10/29/custom-behaviors-in-erlang/)
9
- keyword. Basically, you define a `behavior_info` then drop a `behavior` call within a class.
10
- Forget to implement a required method and Ruby will let you know. See the examples below for details.
11
-
12
- ## Usage
13
-
14
- Require the gem
15
-
16
- ```ruby
17
- require 'functional'
18
- ```
19
-
20
- ### behavior_info
21
-
22
- Next, declare a behavior using the `behavior_info` function (this function should sit outside
23
- of any module/class definition, but will probably work regardless). The first parameter to
24
- `behavior_info` (or `behaviour_info`) is a symbol name for the behavior. The remaining parameter
25
- is a hash of function names and their arity:
26
-
27
- ```ruby
28
- behaviour_info(:gen_foo, foo: 0, bar: 1, baz: 2)
29
-
30
- # -or (for the Java/C# crowd)
31
-
32
- interface(:gen_foo, foo: 0, bar: 1, baz: 2)
33
- ```
34
-
35
- Each function name can be listed only once and the arity must follow the rules of the
36
- [Method#arity](http://ruby-doc.org/core-1.9.3/Method.html#method-i-arity) function.
37
- Though not explicitly documented, block arguments do not count toward a method's arity.
38
- methods defined using this gem's `defn` function will always have an arity of -1,
39
- regardless of how many overloads are defined.
40
-
41
- To specify class/module methods prepend the methid name with 'self_'
42
-
43
- ```ruby
44
- behaviour_info(:gen_foo, self_foo: 0, self_bar: 1, baz: 2)
45
- ```
46
-
47
- ### behavior
48
-
49
- To enforce a behavior on a class simply call the `behavior` function within the class,
50
- passing the name of the desired behavior:
51
-
52
- ```ruby
53
- class Foo
54
- behavior(:gen_foo)
55
- ...
56
- end
57
-
58
- # or use the idiomatic Erlang spelling
59
- class Bar
60
- behaviour(:gen_foo)
61
- ...
62
- end
63
-
64
- # or use the idiomatic Rails syntax
65
- class Baz
66
- behaves_as :gen_foo
67
- ...
68
- end
69
- ```
70
-
71
- Make sure you the implement the required methods in your class. If you don't, Ruby will
72
- raise an exception when you try to create an object from the class:
73
-
74
- ```ruby
75
- Baz.new #=> ArgumentError: undefined callback functions in Baz (behavior 'gen_foo')
76
- ```
77
-
78
- A class may support multiple behaviors:
79
-
80
- ```ruby
81
- behavior_info(:gen_foo, foo: 0)
82
- behavior_info(:gen_bar, bar: 1)
83
-
84
- class FooBar
85
- behavior(:gen_foo)
86
- behavior(:gen_bar)
87
- ...
88
- end
89
- ```
90
-
91
- Inheritance and module inclusion are supported as well:
92
-
93
- ```ruby
94
- behavior_info(:gen_foo, foo: 0)
95
- behavior_info(:gen_bar, bar: 0)
96
-
97
- class Foo
98
- behavior(:gen_foo)
99
- def foo() nil; end
100
- end
101
-
102
- module Bar
103
- behavior(:gen_bar)
104
- def bar() nil; end
105
- end
106
-
107
- class FooBar < Foo
108
- include Bar
109
- end
110
-
111
- foobar = FooBar.new
112
-
113
- foobar.behaves_as?(:gen_foo) #=> true
114
- foobar.behaves_as?(:gen_bar) #=> true
115
- ```
116
-
117
- ### behaves_as?
118
-
119
- As an added bonus, Ruby [Object](http://ruby-doc.org/core-1.9.3/Object.html) will be
120
- monkey-patched with a `behaves_as?` predicate method.
121
-
122
- ## Example
123
-
124
- A complete example:
125
-
126
- ```ruby
127
- behaviour_info(:gen_foo, self_foo: 0, bar: 1, baz: 2, boom: -1, bam: :any)
128
-
129
- class Foo
130
- behavior(:gen_foo)
131
-
132
- def self.foo
133
- return 'foo/0'
134
- end
135
-
136
- def bar(one, &block)
137
- return 'bar/1'
138
- end
139
-
140
- def baz(one, two)
141
- return 'baz/2'
142
- end
143
-
144
- def boom(*args)
145
- return 'boom/-1'
146
- end
147
-
148
- def bam
149
- return 'bam!'
150
- end
151
- end
152
-
153
- foo = Foo.new
154
-
155
- Foo.behaves_as? :gen_foo #=> true
156
- foo.behaves_as? :gen_foo #=> true
157
- foo.behaves_as?(:bogus) #=> false
158
- 'foo'.behaves_as? :gen_foo #=> false
159
- ```
160
-
161
- ## Copyright
162
-
163
- *Functional Ruby* is Copyright &copy; 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
164
- It is free software and may be redistributed under the terms specified in the LICENSE file.
165
-
166
- ## License
167
-
168
- Released under the MIT license.
169
-
170
- http://www.opensource.org/licenses/mit-license.php
171
-
172
- > Permission is hereby granted, free of charge, to any person obtaining a copy
173
- > of this software and associated documentation files (the "Software"), to deal
174
- > in the Software without restriction, including without limitation the rights
175
- > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
176
- > copies of the Software, and to permit persons to whom the Software is
177
- > furnished to do so, subject to the following conditions:
178
- >
179
- > The above copyright notice and this permission notice shall be included in
180
- > all copies or substantial portions of the Software.
181
- >
182
- > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
183
- > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
184
- > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
185
- > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
186
- > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
187
- > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
188
- > THE SOFTWARE.
@@ -1,32 +0,0 @@
1
- # Catalog
2
-
3
- TBD...
4
-
5
- ## Copyright
6
-
7
- *Functional Ruby* is Copyright &copy; 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
8
- It is free software and may be redistributed under the terms specified in the LICENSE file.
9
-
10
- ## License
11
-
12
- Released under the MIT license.
13
-
14
- http://www.opensource.org/licenses/mit-license.php
15
-
16
- > Permission is hereby granted, free of charge, to any person obtaining a copy
17
- > of this software and associated documentation files (the "Software"), to deal
18
- > in the Software without restriction, including without limitation the rights
19
- > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20
- > copies of the Software, and to permit persons to whom the Software is
21
- > furnished to do so, subject to the following conditions:
22
- >
23
- > The above copyright notice and this permission notice shall be included in
24
- > all copies or substantial portions of the Software.
25
- >
26
- > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27
- > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28
- > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29
- > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30
- > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31
- > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
32
- > THE SOFTWARE.
@@ -1,32 +0,0 @@
1
- # Collection
2
-
3
- TBD...
4
-
5
- ## Copyright
6
-
7
- *Functional Ruby* is Copyright &copy; 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
8
- It is free software and may be redistributed under the terms specified in the LICENSE file.
9
-
10
- ## License
11
-
12
- Released under the MIT license.
13
-
14
- http://www.opensource.org/licenses/mit-license.php
15
-
16
- > Permission is hereby granted, free of charge, to any person obtaining a copy
17
- > of this software and associated documentation files (the "Software"), to deal
18
- > in the Software without restriction, including without limitation the rights
19
- > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20
- > copies of the Software, and to permit persons to whom the Software is
21
- > furnished to do so, subject to the following conditions:
22
- >
23
- > The above copyright notice and this permission notice shall be included in
24
- > all copies or substantial portions of the Software.
25
- >
26
- > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27
- > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28
- > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29
- > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30
- > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31
- > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
32
- > THE SOFTWARE.
@@ -1,32 +0,0 @@
1
- # Inflect
2
-
3
- TBD...
4
-
5
- ## Copyright
6
-
7
- *Functional Ruby* is Copyright &copy; 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
8
- It is free software and may be redistributed under the terms specified in the LICENSE file.
9
-
10
- ## License
11
-
12
- Released under the MIT license.
13
-
14
- http://www.opensource.org/licenses/mit-license.php
15
-
16
- > Permission is hereby granted, free of charge, to any person obtaining a copy
17
- > of this software and associated documentation files (the "Software"), to deal
18
- > in the Software without restriction, including without limitation the rights
19
- > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20
- > copies of the Software, and to permit persons to whom the Software is
21
- > furnished to do so, subject to the following conditions:
22
- >
23
- > The above copyright notice and this permission notice shall be included in
24
- > all copies or substantial portions of the Software.
25
- >
26
- > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27
- > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28
- > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29
- > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30
- > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31
- > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
32
- > THE SOFTWARE.
@@ -1,512 +0,0 @@
1
- # Erlang-style Pattern Matching
2
-
3
- As much as I love Ruby I've always been a little disappointed that Ruby doesn't
4
- support function overloading. Function overloading tends to reduce branching
5
- and keep function signatures simpler. No sweat, I learned to do without. Then
6
- I started programming in Erlang. My favorite Erlang feature is, without
7
- question, pattern matching. Pattern matching is like function overloading
8
- cranked to 11. So one day I was musing on Twitter that I'd like to see
9
- Erlang-stype pattern matching in Ruby and one of my friends responded
10
- "Build it!" So I did. And here it is.
11
-
12
-
13
- ## Features
14
-
15
- * Pattern matching for instance methods.
16
- * Pattern matching for object constructors.
17
- * Parameter count matching
18
- * Matching against primitive values
19
- * Matching by class/datatype
20
- * Matching against specific key/vaue pairs in hashes
21
- * Matching against the presence of keys within hashes
22
- * Implicit hash for last parameter
23
- * Variable-length parameter lists
24
- * Guard clauses
25
- * Recursive calls to other pattern matches
26
- * Recursive calls to superclass pattern matches
27
- * Recursive calls to superclass methods
28
- * Dispatching to superclass methods when no match is found
29
- * Reasonable error messages when no match is found
30
-
31
- ## Usage
32
-
33
- First, familiarize yourself with Erlang [pattern matching](http://learnyousomeerlang.com/syntax-in-functions#pattern-matching).
34
- This gem may not make much sense if you don't understand how Erlang dispatches functions.
35
-
36
- In the Ruby class file where you want to use pattern matching, require the *functional-ruby* gem:
37
-
38
- ```ruby
39
- require 'functional'
40
- ```
41
-
42
- Then include `PatternMatching` in your class:
43
-
44
- ```ruby
45
- require 'functional'
46
-
47
- class Foo
48
- include PatternMatching
49
-
50
- ...
51
-
52
- end
53
- ```
54
-
55
- You can then define functions with `defn` instead of the normal *def* statement.
56
- The syntax for `defn` is:
57
-
58
- ```ruby
59
- defn(:symbol_name_of_function, zero, or, more, parameters) { |block, arguments|
60
- # code to execute
61
- }
62
- ```
63
- You can then call your new function just like any other:
64
-
65
- ```ruby
66
- require 'functional/pattern_matching'
67
-
68
- class Foo
69
- include PatternMatching
70
-
71
- defn(:hello) {
72
- puts "Hello, World!"
73
- }
74
- end
75
-
76
- foo = Foo.new
77
- foo.hello #=> "Hello, World!"
78
- ```
79
-
80
- Patterns to match against are included in the parameter list:
81
-
82
- ```ruby
83
- defn(:greet, :male) {
84
- puts "Hello, sir!"
85
- }
86
-
87
- defn(:greet, :female) {
88
- puts "Hello, ma'am!"
89
- }
90
-
91
- ...
92
-
93
- foo.greet(:male) #=> "Hello, sir!"
94
- foo.greet(:female) #=> "Hello, ma'am!"
95
- ```
96
-
97
- If a particular method call can not be matched a *NoMethodError* is thrown with
98
- a reasonably helpful error message:
99
-
100
- ```ruby
101
- foo.greet(:unknown) #=> NoMethodError: no method `greet` matching [:unknown] found for class Foo
102
- foo.greet #=> NoMethodError: no method `greet` matching [] found for class Foo
103
- ```
104
-
105
- Parameters that are expected to exist but that can take any value are considered
106
- *unbound* parameters. Unbound parameters are specified by the `_` underscore
107
- character or `UNBOUND`:
108
-
109
- ```ruby
110
- defn(:greet, _) do |name|
111
- "Hello, #{name}!"
112
- end
113
-
114
- defn(:greet, UNBOUND, UNBOUND) do |first, last|
115
- "Hello, #{first} #{last}!"
116
- end
117
-
118
- ...
119
-
120
- foo.greet('Jerry') #=> "Hello, Jerry!"
121
- ```
122
-
123
- All unbound parameters will be passed to the block in the order they are specified in the definition:
124
-
125
- ```ruby
126
- defn(:greet, _, _) do |first, last|
127
- "Hello, #{first} #{last}!"
128
- end
129
-
130
- ...
131
-
132
- foo.greet('Jerry', "D'Antonio") #=> "Hello, Jerry D'Antonio!"
133
- ```
134
-
135
- If for some reason you don't care about one or more unbound parameters within
136
- the block you can use the `_` underscore character in the block parameters list
137
- as well:
138
-
139
- ```ruby
140
- defn(:greet, _, _, _) do |first, _, last|
141
- "Hello, #{first} #{last}!"
142
- end
143
-
144
- ...
145
-
146
- foo.greet('Jerry', "I'm not going to tell you my middle name!", "D'Antonio") #=> "Hello, Jerry D'Antonio!"
147
- ```
148
-
149
- Hash parameters can match against specific keys and either bound or unbound parameters. This allows for
150
- function dispatch by hash parameters without having to dig through the hash:
151
-
152
- ```ruby
153
- defn(:hashable, {foo: :bar}) { |opts|
154
- :foo_bar
155
- }
156
- defn(:hashable, {foo: _}) { |f|
157
- f
158
- }
159
-
160
- ...
161
-
162
- foo.hashable({foo: :bar}) #=> :foo_bar
163
- foo.hashable({foo: :baz}) #=> :baz
164
- ```
165
-
166
- The Ruby idiom of the final parameter being a hash is also supported:
167
-
168
- ```ruby
169
- defn(:options, _) { |opts|
170
- opts
171
- }
172
-
173
- ...
174
-
175
- foo.options(bar: :baz, one: 1, many: 2)
176
- ```
177
-
178
- As is the Ruby idiom of variable-length argument lists. The constant `ALL` as the last parameter
179
- will match one or more arguments and pass them to the block as an array:
180
-
181
- ```ruby
182
- defn(:baz, Integer, ALL) { |int, args|
183
- [int, args]
184
- }
185
- defn(:baz, ALL) { |args|
186
- args
187
- }
188
- ```
189
-
190
- Superclass polymorphism is supported as well. If an object cannot match a method
191
- signature it will defer to the parent class:
192
-
193
- ```ruby
194
- class Bar
195
- def greet
196
- return 'Hello, World!'
197
- end
198
- end
199
-
200
- class Foo < Bar
201
- include PatternMatching
202
-
203
- defn(:greet, _) do |name|
204
- "Hello, #{name}!"
205
- end
206
- end
207
-
208
- ...
209
-
210
- foo.greet('Jerry') #=> "Hello, Jerry!"
211
- foo.greet #=> "Hello, World!"
212
- ```
213
-
214
- Guard clauses in Erlang are defined with `when` clauses between the parameter list and the function body.
215
- In Ruby, guard clauses are defined by chaining a call to `when` onto the the `defn` call and passing
216
- a block. If the guard clause evaluates to true then the function will match. If the guard evaluates
217
- to false the function will not match and pattern matching will continue:
218
-
219
- Erlang:
220
-
221
- ```erlang
222
- old_enough(X) when X >= 16 -> true;
223
- old_enough(_) -> false.
224
- ```
225
-
226
- Ruby:
227
-
228
- ```ruby
229
- defn(:old_enough, _){ true }.when{|x| x >= 16 }
230
- defn(:old_enough, _){ false }
231
- ```
232
-
233
- ### Order Matters
234
-
235
- As with Erlang, the order of pattern matches is significant. Patterns will be matched
236
- *in the order declared* and the first match will be used. If a particular function call
237
- can be matched by more than one pattern, the *first matched pattern* will be used. It
238
- is the programmer's responsibility to ensure patterns are declared in the correct order.
239
-
240
- ### Blocks and Procs and Lambdas, oh my!
241
-
242
- When using this gem it is critical to remember that `defn` takes a block and
243
- that blocks in Ruby have special rules. There are [plenty](https://www.google.com/search?q=ruby+block+proc+lambda)
244
- of good tutorials on the web explaining [blocks](http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/)
245
- and [Procs](https://coderwall.com/p/_-_mha) and [lambdas](http://railsguru.org/2010/03/learn-ruby-procs-blocks-lambda/)
246
- in Ruby. Please read them. Please don't submit a bug report if you use a
247
- `return` statement within your `defn` and your code blows up with a
248
- [LocalJumpError](http://ruby-doc.org/core-2.0/LocalJumpError.html).
249
-
250
- ### Examples
251
-
252
- For more examples see the integration tests in *spec/integration_spec.rb*.
253
-
254
- #### Simple Functions
255
-
256
- 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/).
257
-
258
- Erlang:
259
-
260
- ```erlang
261
- greet(male, Name) ->
262
- io:format("Hello, Mr. ~s!", [Name]);
263
- greet(female, Name) ->
264
- io:format("Hello, Mrs. ~s!", [Name]);
265
- greet(_, Name) ->
266
- io:format("Hello, ~s!", [Name]).
267
- ```
268
-
269
- Ruby:
270
-
271
- ```ruby
272
- require 'functional/pattern_matching'
273
-
274
- class Foo
275
- include PatternMatching
276
-
277
- defn(:greet, _) do |name|
278
- "Hello, #{name}!"
279
- end
280
-
281
- defn(:greet, :male, _) { |name|
282
- "Hello, Mr. #{name}!"
283
- }
284
- defn(:greet, :female, _) { |name|
285
- "Hello, Ms. #{name}!"
286
- }
287
- defn(:greet, _, _) { |_, name|
288
- "Hello, #{name}!"
289
- }
290
- end
291
- ```
292
-
293
- #### Simple Functions with Overloading
294
-
295
- 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/).
296
-
297
- Erlang:
298
-
299
- ```erlang
300
- greet(Name) ->
301
- io:format("Hello, ~s!", [Name]).
302
-
303
- greet(male, Name) ->
304
- io:format("Hello, Mr. ~s!", [Name]);
305
- greet(female, Name) ->
306
- io:format("Hello, Mrs. ~s!", [Name]);
307
- greet(_, Name) ->
308
- io:format("Hello, ~s!", [Name]).
309
- ```
310
-
311
- Ruby:
312
-
313
- ```ruby
314
- require 'functional/pattern_matching'
315
-
316
- class Foo
317
- include PatternMatching
318
-
319
- defn(:greet, _) do |name|
320
- "Hello, #{name}!"
321
- end
322
-
323
- defn(:greet, :male, _) { |name|
324
- "Hello, Mr. #{name}!"
325
- }
326
- defn(:greet, :female, _) { |name|
327
- "Hello, Ms. #{name}!"
328
- }
329
- defn(:greet, nil, _) { |name|
330
- "Goodbye, #{name}!"
331
- }
332
- defn(:greet, _, _) { |_, name|
333
- "Hello, #{name}!"
334
- }
335
- end
336
- ```
337
-
338
- #### Constructor Overloading
339
-
340
- ```ruby
341
- require 'functional/pattern_matching'
342
-
343
- class Foo
344
- include PatternMatching
345
-
346
- defn(:initialize) { @name = 'baz' }
347
- defn(:initialize, _) {|name| @name = name.to_s }
348
- end
349
- ```
350
-
351
- #### Matching by Class/Datatype
352
-
353
- ```ruby
354
- require 'functional/pattern_matching'
355
-
356
- class Foo
357
- include PatternMatching
358
-
359
- defn(:concat, Integer, Integer) { |first, second|
360
- first + second
361
- }
362
- defn(:concat, Integer, String) { |first, second|
363
- "#{first} #{second}"
364
- }
365
- defn(:concat, String, String) { |first, second|
366
- first + second
367
- }
368
- defn(:concat, Integer, _) { |first, second|
369
- first + second.to_i
370
- }
371
- end
372
- ```
373
-
374
- #### Matching a Hash Parameter
375
-
376
- ```ruby
377
- require 'functional/pattern_matching'
378
-
379
- class Foo
380
- include PatternMatching
381
-
382
- defn(:hashable, {foo: :bar}) { |opts|
383
- # matches any hash with key :foo and value :bar
384
- :foo_bar
385
- }
386
- defn(:hashable, {foo: _, bar: _}) { |f, b|
387
- # matches any hash with keys :foo and :bar
388
- # passes the values associated with those keys to the block
389
- [f, b]
390
- }
391
- defn(:hashable, {foo: _}) { |f|
392
- # matches any hash with key :foo
393
- # passes the value associated with that key to the block
394
- # must appear AFTER the prior match or it will override that one
395
- f
396
- }
397
- defn(:hashable, {}) { ||
398
- # matches an empty hash
399
- :empty
400
- }
401
- defn(:hashable, _) { |opts|
402
- # matches any hash (or any other value)
403
- opts
404
- }
405
- end
406
-
407
- ...
408
-
409
- foo.hashable({foo: :bar}) #=> :foo_bar
410
- foo.hashable({foo: :baz}) #=> :baz
411
- foo.hashable({foo: 1, bar: 2}) #=> [1, 2]
412
- foo.hashable({foo: 1, baz: 2}) #=> 1
413
- foo.hashable({bar: :baz}) #=> {bar: :baz}
414
- foo.hashable({}) #=> :empty
415
- ```
416
-
417
- #### Variable Length Argument Lists with ALL
418
-
419
- ```ruby
420
- defn(:all, :one, ALL) { |args|
421
- args
422
- }
423
- defn(:all, :one, Integer, ALL) { |int, args|
424
- [int, args]
425
- }
426
- defn(:all, 1, _, ALL) { |var, args|
427
- [var, args]
428
- }
429
- defn(:all, ALL) { | args|
430
- args
431
- }
432
-
433
- ...
434
-
435
- foo.all(:one, 'a', 'bee', :see) #=> ['a', 'bee', :see]
436
- foo.all(:one, 1, 'bee', :see) #=> [1, 'bee', :see]
437
- foo.all(1, 'a', 'bee', :see) #=> ['a', ['bee', :see]]
438
- foo.all('a', 'bee', :see) #=> ['a', 'bee', :see]
439
- foo.all() #=> NoMethodError: no method `all` matching [] found for class Foo
440
- ```
441
-
442
- #### Guard Clauses
443
-
444
- These examples are based on [Syntax in defnctions: Pattern Matching](http://learnyousomeerlang.com/syntax-in-defnctions)
445
- in [Learn You Some Erlang for Great Good!](http://learnyousomeerlang.com/).
446
-
447
- Erlang:
448
-
449
- ```erlang
450
- old_enough(X) when X >= 16 -> true;
451
- old_enough(_) -> false.
452
-
453
- right_age(X) when X >= 16, X =< 104 ->
454
- true;
455
- right_age(_) ->
456
- false.
457
-
458
- wrong_age(X) when X < 16; X > 104 ->
459
- true;
460
- wrong_age(_) ->
461
- false.
462
- ```
463
-
464
- ```ruby
465
- defn(:old_enough, _){ true }.when{|x| x >= 16 }
466
- defn(:old_enough, _){ false }
467
-
468
- defn(:right_age, _) {
469
- true
470
- }.when{|x| x >= 16 && x <= 104 }
471
-
472
- defn(:right_age, _) {
473
- false
474
- }
475
-
476
- defn(:wrong_age, _) {
477
- false
478
- }.when{|x| x < 16 || x > 104 }
479
-
480
- defn(:wrong_age, _) {
481
- true
482
- }
483
- ```
484
-
485
- ## Copyright
486
-
487
- *Functional Ruby* is Copyright &copy; 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
488
- It is free software and may be redistributed under the terms specified in the LICENSE file.
489
-
490
- ## License
491
-
492
- Released under the MIT license.
493
-
494
- http://www.opensource.org/licenses/mit-license.php
495
-
496
- > Permission is hereby granted, free of charge, to any person obtaining a copy
497
- > of this software and associated documentation files (the "Software"), to deal
498
- > in the Software without restriction, including without limitation the rights
499
- > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
500
- > copies of the Software, and to permit persons to whom the Software is
501
- > furnished to do so, subject to the following conditions:
502
- >
503
- > The above copyright notice and this permission notice shall be included in
504
- > all copies or substantial portions of the Software.
505
- >
506
- > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
507
- > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
508
- > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
509
- > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
510
- > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
511
- > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
512
- > THE SOFTWARE.