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
@@ -1,4 +1,3 @@
1
- require 'spec_helper'
2
1
  require 'rspec/expectations'
3
2
 
4
3
  RSpec::Matchers.define :be_a_different_tuple_than do |expected|
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  module Functional
4
2
 
5
3
  describe TypeCheck do
@@ -1,4 +1,3 @@
1
- require 'spec_helper'
2
1
  require_relative 'abstract_struct_shared'
3
2
 
4
3
  module Functional
@@ -1,4 +1,3 @@
1
- require 'spec_helper'
2
1
  require 'ostruct'
3
2
 
4
3
  module Functional
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: functional-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jerry D'Antonio
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-12 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: bundler
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
11
+ date: 2015-07-10 00:00:00.000000000 Z
12
+ dependencies: []
27
13
  description: |2
28
14
  A gem for adding functional programming tools to Ruby. Inspired by Erlang, Clojure, Haskell, and Functional Java.
29
15
  email: jerry.dantonio@gmail.com
@@ -33,20 +19,18 @@ extra_rdoc_files:
33
19
  - README.md
34
20
  - LICENSE
35
21
  - CHANGELOG.md
36
- - doc/memo.txt
37
- - doc/pattern_matching.txt
38
- - doc/protocol.txt
39
- - doc/record.txt
40
- - doc/thread_safety.txt
22
+ - doc/memo.md
23
+ - doc/pattern_matching.md
24
+ - doc/protocol.md
25
+ - doc/record.md
41
26
  files:
42
27
  - CHANGELOG.md
43
28
  - LICENSE
44
29
  - README.md
45
- - doc/memo.txt
46
- - doc/pattern_matching.txt
47
- - doc/protocol.txt
48
- - doc/record.txt
49
- - doc/thread_safety.txt
30
+ - doc/memo.md
31
+ - doc/pattern_matching.md
32
+ - doc/protocol.md
33
+ - doc/record.md
50
34
  - lib/functional.rb
51
35
  - lib/functional/abstract_struct.rb
52
36
  - lib/functional/delay.rb
@@ -60,6 +44,7 @@ files:
60
44
  - lib/functional/protocol.rb
61
45
  - lib/functional/protocol_info.rb
62
46
  - lib/functional/record.rb
47
+ - lib/functional/synchronization.rb
63
48
  - lib/functional/tuple.rb
64
49
  - lib/functional/type_check.rb
65
50
  - lib/functional/union.rb
@@ -96,7 +81,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
96
81
  requirements:
97
82
  - - ">="
98
83
  - !ruby/object:Gem::Version
99
- version: 1.9.3
84
+ version: 2.0.0
100
85
  required_rubygems_version: !ruby/object:Gem::Requirement
101
86
  requirements:
102
87
  - - ">="
@@ -104,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
104
89
  version: '0'
105
90
  requirements: []
106
91
  rubyforge_project:
107
- rubygems_version: 2.2.2
92
+ rubygems_version: 2.4.8
108
93
  signing_key:
109
94
  specification_version: 4
110
95
  summary: Erlang, Clojure, Haskell, and Functional Java inspired functional programming
@@ -1,192 +0,0 @@
1
- # @!macro [new] 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
@@ -1,485 +0,0 @@
1
- # @!macro [new] pattern_matching
2
- #
3
- # ## Features
4
- #
5
- # * Pattern matching for instance methods.
6
- # * Pattern matching for object constructors.
7
- # * Parameter count matching
8
- # * Matching against primitive values
9
- # * Matching by class/datatype
10
- # * Matching against specific key/vaue pairs in hashes
11
- # * Matching against the presence of keys within hashes
12
- # * Implicit hash for last parameter
13
- # * Variable-length parameter lists
14
- # * Guard clauses
15
- # * Recursive calls to other pattern matches
16
- # * Recursive calls to superclass pattern matches
17
- # * Recursive calls to superclass methods
18
- # * Dispatching to superclass methods when no match is found
19
- # * Reasonable error messages when no match is found
20
- #
21
- # ## Usage
22
- #
23
- # First, familiarize yourself with Erlang [pattern matching](http://learnyousomeerlang.com/syntax-in-functions#pattern-matching).
24
- # This gem may not make much sense if you don't understand how Erlang dispatches functions.
25
- #
26
- # In the Ruby class file where you want to use pattern matching, require the *functional-ruby* gem:
27
- #
28
- # ```ruby
29
- # require 'functional'
30
- # ```
31
- #
32
- # Then include `Functional::PatternMatching` in your class:
33
- #
34
- # ```ruby
35
- # require 'functional'
36
- #
37
- # class Foo
38
- # include Functional::PatternMatching
39
- #
40
- # ...
41
- #
42
- # end
43
- # ```
44
- #
45
- # You can then define functions with `defn` instead of the normal *def* statement.
46
- # The syntax for `defn` is:
47
- #
48
- # ```ruby
49
- # defn(:symbol_name_of_function, zero, or, more, parameters) { |block, arguments|
50
- # # code to execute
51
- # }
52
- # ```
53
- # You can then call your new function just like any other:
54
- #
55
- # ```ruby
56
- # require 'functional/pattern_matching'
57
- #
58
- # class Foo
59
- # include Functional::PatternMatching
60
- #
61
- # defn(:hello) {
62
- # puts "Hello, World!"
63
- # }
64
- # end
65
- #
66
- # foo = Foo.new
67
- # foo.hello #=> "Hello, World!"
68
- # ```
69
- #
70
- # Patterns to match against are included in the parameter list:
71
- #
72
- # ```ruby
73
- # defn(:greet, :male) {
74
- # puts "Hello, sir!"
75
- # }
76
- #
77
- # defn(:greet, :female) {
78
- # puts "Hello, ma'am!"
79
- # }
80
- #
81
- # ...
82
- #
83
- # foo.greet(:male) #=> "Hello, sir!"
84
- # foo.greet(:female) #=> "Hello, ma'am!"
85
- # ```
86
- #
87
- # If a particular method call can not be matched a *NoMethodError* is thrown with
88
- # a reasonably helpful error message:
89
- #
90
- # ```ruby
91
- # foo.greet(:unknown) #=> NoMethodError: no method `greet` matching [:unknown] found for class Foo
92
- # foo.greet #=> NoMethodError: no method `greet` matching [] found for class Foo
93
- # ```
94
- #
95
- # Parameters that are expected to exist but that can take any value are considered
96
- # *unbound* parameters. Unbound parameters are specified by the `_` underscore
97
- # character or `UNBOUND`:
98
- #
99
- # ```ruby
100
- # defn(:greet, _) do |name|
101
- # "Hello, #{name}!"
102
- # end
103
- #
104
- # defn(:greet, UNBOUND, UNBOUND) do |first, last|
105
- # "Hello, #{first} #{last}!"
106
- # end
107
- #
108
- # ...
109
- #
110
- # foo.greet('Jerry') #=> "Hello, Jerry!"
111
- # ```
112
- #
113
- # All unbound parameters will be passed to the block in the order they are specified in the definition:
114
- #
115
- # ```ruby
116
- # defn(:greet, _, _) do |first, last|
117
- # "Hello, #{first} #{last}!"
118
- # end
119
- #
120
- # ...
121
- #
122
- # foo.greet('Jerry', "D'Antonio") #=> "Hello, Jerry D'Antonio!"
123
- # ```
124
- #
125
- # If for some reason you don't care about one or more unbound parameters within
126
- # the block you can use the `_` underscore character in the block parameters list
127
- # as well:
128
- #
129
- # ```ruby
130
- # defn(:greet, _, _, _) do |first, _, last|
131
- # "Hello, #{first} #{last}!"
132
- # end
133
- #
134
- # ...
135
- #
136
- # foo.greet('Jerry', "I'm not going to tell you my middle name!", "D'Antonio") #=> "Hello, Jerry D'Antonio!"
137
- # ```
138
- #
139
- # Hash parameters can match against specific keys and either bound or unbound parameters. This allows for
140
- # function dispatch by hash parameters without having to dig through the hash:
141
- #
142
- # ```ruby
143
- # defn(:hashable, {foo: :bar}) { |opts|
144
- # :foo_bar
145
- # }
146
- # defn(:hashable, {foo: _}) { |f|
147
- # f
148
- # }
149
- #
150
- # ...
151
- #
152
- # foo.hashable({foo: :bar}) #=> :foo_bar
153
- # foo.hashable({foo: :baz}) #=> :baz
154
- # ```
155
- #
156
- # The Ruby idiom of the final parameter being a hash is also supported:
157
- #
158
- # ```ruby
159
- # defn(:options, _) { |opts|
160
- # opts
161
- # }
162
- #
163
- # ...
164
- #
165
- # foo.options(bar: :baz, one: 1, many: 2)
166
- # ```
167
- #
168
- # As is the Ruby idiom of variable-length argument lists. The constant `ALL` as the last parameter
169
- # will match one or more arguments and pass them to the block as an array:
170
- #
171
- # ```ruby
172
- # defn(:baz, Integer, ALL) { |int, args|
173
- # [int, args]
174
- # }
175
- # defn(:baz, ALL) { |args|
176
- # args
177
- # }
178
- # ```
179
- #
180
- # Superclass polymorphism is supported as well. If an object cannot match a method
181
- # signature it will defer to the parent class:
182
- #
183
- # ```ruby
184
- # class Bar
185
- # def greet
186
- # return 'Hello, World!'
187
- # end
188
- # end
189
- #
190
- # class Foo < Bar
191
- # include Functional::PatternMatching
192
- #
193
- # defn(:greet, _) do |name|
194
- # "Hello, #{name}!"
195
- # end
196
- # end
197
- #
198
- # ...
199
- #
200
- # foo.greet('Jerry') #=> "Hello, Jerry!"
201
- # foo.greet #=> "Hello, World!"
202
- # ```
203
- #
204
- # Guard clauses in Erlang are defined with `when` clauses between the parameter list and the function body.
205
- # In Ruby, guard clauses are defined by chaining a call to `when` onto the the `defn` call and passing
206
- # a block. If the guard clause evaluates to true then the function will match. If the guard evaluates
207
- # to false the function will not match and pattern matching will continue:
208
- #
209
- # Erlang:
210
- #
211
- # ```erlang
212
- # old_enough(X) when X >= 16 -> true;
213
- # old_enough(_) -> false.
214
- # ```
215
- #
216
- # Ruby:
217
- #
218
- # ```ruby
219
- # defn(:old_enough, _){ true }.when{|x| x >= 16 }
220
- # defn(:old_enough, _){ false }
221
- # ```
222
- #
223
- # ### Order Matters
224
- #
225
- # As with Erlang, the order of pattern matches is significant. Patterns will be matched
226
- # *in the order declared* and the first match will be used. If a particular function call
227
- # can be matched by more than one pattern, the *first matched pattern* will be used. It
228
- # is the programmer's responsibility to ensure patterns are declared in the correct order.
229
- #
230
- # ### Blocks and Procs and Lambdas, oh my!
231
- #
232
- # When using this gem it is critical to remember that `defn` takes a block and
233
- # that blocks in Ruby have special rules. There are [plenty](https://www.google.com/search?q=ruby+block+proc+lambda)
234
- # of good tutorials on the web explaining [blocks](http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/)
235
- # and [Procs](https://coderwall.com/p/_-_mha) and [lambdas](http://railsguru.org/2010/03/learn-ruby-procs-blocks-lambda/)
236
- # in Ruby. Please read them. Please don't submit a bug report if you use a
237
- # `return` statement within your `defn` and your code blows up with a
238
- # [LocalJumpError](http://ruby-doc.org/core-2.0/LocalJumpError.html).
239
- #
240
- # ### Examples
241
- #
242
- # For more examples see the integration tests in *spec/integration_spec.rb*.
243
- #
244
- # #### Simple Functions
245
- #
246
- # 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/).
247
- #
248
- # Erlang:
249
- #
250
- # ```erlang
251
- # greet(male, Name) ->
252
- # io:format("Hello, Mr. ~s!", [Name]);
253
- # greet(female, Name) ->
254
- # io:format("Hello, Mrs. ~s!", [Name]);
255
- # greet(_, Name) ->
256
- # io:format("Hello, ~s!", [Name]).
257
- # ```
258
- #
259
- # Ruby:
260
- #
261
- # ```ruby
262
- # require 'functional/pattern_matching'
263
- #
264
- # class Foo
265
- # include Functional::PatternMatching
266
- #
267
- # defn(:greet, _) do |name|
268
- # "Hello, #{name}!"
269
- # end
270
- #
271
- # defn(:greet, :male, _) { |name|
272
- # "Hello, Mr. #{name}!"
273
- # }
274
- # defn(:greet, :female, _) { |name|
275
- # "Hello, Ms. #{name}!"
276
- # }
277
- # defn(:greet, _, _) { |_, name|
278
- # "Hello, #{name}!"
279
- # }
280
- # end
281
- # ```
282
- #
283
- # #### Simple Functions with Overloading
284
- #
285
- # 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/).
286
- #
287
- # Erlang:
288
- #
289
- # ```erlang
290
- # greet(Name) ->
291
- # io:format("Hello, ~s!", [Name]).
292
- #
293
- # greet(male, Name) ->
294
- # io:format("Hello, Mr. ~s!", [Name]);
295
- # greet(female, Name) ->
296
- # io:format("Hello, Mrs. ~s!", [Name]);
297
- # greet(_, Name) ->
298
- # io:format("Hello, ~s!", [Name]).
299
- # ```
300
- #
301
- # Ruby:
302
- #
303
- # ```ruby
304
- # require 'functional/pattern_matching'
305
- #
306
- # class Foo
307
- # include Functional::PatternMatching
308
- #
309
- # defn(:greet, _) do |name|
310
- # "Hello, #{name}!"
311
- # end
312
- #
313
- # defn(:greet, :male, _) { |name|
314
- # "Hello, Mr. #{name}!"
315
- # }
316
- # defn(:greet, :female, _) { |name|
317
- # "Hello, Ms. #{name}!"
318
- # }
319
- # defn(:greet, nil, _) { |name|
320
- # "Goodbye, #{name}!"
321
- # }
322
- # defn(:greet, _, _) { |_, name|
323
- # "Hello, #{name}!"
324
- # }
325
- # end
326
- # ```
327
- #
328
- # #### Constructor Overloading
329
- #
330
- # ```ruby
331
- # require 'functional/pattern_matching'
332
- #
333
- # class Foo
334
- # include Functional::PatternMatching
335
- #
336
- # defn(:initialize) { @name = 'baz' }
337
- # defn(:initialize, _) {|name| @name = name.to_s }
338
- # end
339
- # ```
340
- #
341
- # #### Matching by Class/Datatype
342
- #
343
- # ```ruby
344
- # require 'functional/pattern_matching'
345
- #
346
- # class Foo
347
- # include Functional::PatternMatching
348
- #
349
- # defn(:concat, Integer, Integer) { |first, second|
350
- # first + second
351
- # }
352
- # defn(:concat, Integer, String) { |first, second|
353
- # "#{first} #{second}"
354
- # }
355
- # defn(:concat, String, String) { |first, second|
356
- # first + second
357
- # }
358
- # defn(:concat, Integer, _) { |first, second|
359
- # first + second.to_i
360
- # }
361
- # end
362
- # ```
363
- #
364
- # #### Matching a Hash Parameter
365
- #
366
- # ```ruby
367
- # require 'functional/pattern_matching'
368
- #
369
- # class Foo
370
- # include Functional::PatternMatching
371
- #
372
- # defn(:hashable, {foo: :bar}) { |opts|
373
- # # matches any hash with key :foo and value :bar
374
- # :foo_bar
375
- # }
376
- # defn(:hashable, {foo: _, bar: _}) { |f, b|
377
- # # matches any hash with keys :foo and :bar
378
- # # passes the values associated with those keys to the block
379
- # [f, b]
380
- # }
381
- # defn(:hashable, {foo: _}) { |f|
382
- # # matches any hash with key :foo
383
- # # passes the value associated with that key to the block
384
- # # must appear AFTER the prior match or it will override that one
385
- # f
386
- # }
387
- # defn(:hashable, {}) { ||
388
- # # matches an empty hash
389
- # :empty
390
- # }
391
- # defn(:hashable, _) { |opts|
392
- # # matches any hash (or any other value)
393
- # opts
394
- # }
395
- # end
396
- #
397
- # ...
398
- #
399
- # foo.hashable({foo: :bar}) #=> :foo_bar
400
- # foo.hashable({foo: :baz}) #=> :baz
401
- # foo.hashable({foo: 1, bar: 2}) #=> [1, 2]
402
- # foo.hashable({foo: 1, baz: 2}) #=> 1
403
- # foo.hashable({bar: :baz}) #=> {bar: :baz}
404
- # foo.hashable({}) #=> :empty
405
- # ```
406
- #
407
- # #### Variable Length Argument Lists with ALL
408
- #
409
- # ```ruby
410
- # defn(:all, :one, ALL) { |args|
411
- # args
412
- # }
413
- # defn(:all, :one, Integer, ALL) { |int, args|
414
- # [int, args]
415
- # }
416
- # defn(:all, 1, _, ALL) { |var, args|
417
- # [var, args]
418
- # }
419
- # defn(:all, ALL) { | args|
420
- # args
421
- # }
422
- #
423
- # ...
424
- #
425
- # foo.all(:one, 'a', 'bee', :see) #=> ['a', 'bee', :see]
426
- # foo.all(:one, 1, 'bee', :see) #=> [1, 'bee', :see]
427
- # foo.all(1, 'a', 'bee', :see) #=> ['a', ['bee', :see]]
428
- # foo.all('a', 'bee', :see) #=> ['a', 'bee', :see]
429
- # foo.all() #=> NoMethodError: no method `all` matching [] found for class Foo
430
- # ```
431
- #
432
- # #### Guard Clauses
433
- #
434
- # These examples are based on [Syntax in defnctions: Pattern Matching](http://learnyousomeerlang.com/syntax-in-defnctions)
435
- # in [Learn You Some Erlang for Great Good!](http://learnyousomeerlang.com/).
436
- #
437
- # Erlang:
438
- #
439
- # ```erlang
440
- # old_enough(X) when X >= 16 -> true;
441
- # old_enough(_) -> false.
442
- #
443
- # right_age(X) when X >= 16, X =< 104 ->
444
- # true;
445
- # right_age(_) ->
446
- # false.
447
- #
448
- # wrong_age(X) when X < 16; X > 104 ->
449
- # true;
450
- # wrong_age(_) ->
451
- # false.
452
- # ```
453
- #
454
- # ```ruby
455
- # defn(:old_enough, _){ true }.when{|x| x >= 16 }
456
- # defn(:old_enough, _){ false }
457
- #
458
- # defn(:right_age, _) {
459
- # true
460
- # }.when{|x| x >= 16 && x <= 104 }
461
- #
462
- # defn(:right_age, _) {
463
- # false
464
- # }
465
- #
466
- # defn(:wrong_age, _) {
467
- # false
468
- # }.when{|x| x < 16 || x > 104 }
469
- #
470
- # defn(:wrong_age, _) {
471
- # true
472
- # }
473
- # ```
474
- #
475
- # ## Inspiration
476
- #
477
- # Pattern matching has its roots in logic programming languages such as
478
- # [Prolog](http://en.wikipedia.org/wiki/Prolog). Pattern matching is a core
479
- # feature of the [Erlang](http://www.erlang.org/) programming language. A few
480
- # helpful resources are:
481
- #
482
- # * Erlang [modules](http://erlang.org/doc/reference_manual/modules.html)
483
- # * Erlang [pattern matching](http://erlang.org/doc/reference_manual/patterns.html)
484
-
485
-