functional-ruby 0.5.0 → 0.6.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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +154 -562
  3. data/lib/functional/agent.rb +130 -0
  4. data/lib/functional/all.rb +9 -1
  5. data/lib/functional/behavior.rb +72 -39
  6. data/lib/functional/cached_thread_pool.rb +122 -0
  7. data/lib/functional/concurrency.rb +32 -24
  8. data/lib/functional/core.rb +2 -62
  9. data/lib/functional/event.rb +53 -0
  10. data/lib/functional/event_machine_defer_proxy.rb +23 -0
  11. data/lib/functional/fixed_thread_pool.rb +89 -0
  12. data/lib/functional/future.rb +42 -0
  13. data/lib/functional/global_thread_pool.rb +3 -0
  14. data/lib/functional/obligation.rb +121 -0
  15. data/lib/functional/promise.rb +194 -0
  16. data/lib/functional/thread_pool.rb +61 -0
  17. data/lib/functional/utilities.rb +114 -0
  18. data/lib/functional/version.rb +1 -1
  19. data/lib/functional.rb +1 -0
  20. data/lib/functional_ruby.rb +1 -0
  21. data/md/behavior.md +147 -0
  22. data/md/concurrency.md +465 -0
  23. data/md/future.md +32 -0
  24. data/md/obligation.md +32 -0
  25. data/md/pattern_matching.md +512 -0
  26. data/md/promise.md +220 -0
  27. data/md/utilities.md +53 -0
  28. data/spec/functional/agent_spec.rb +405 -0
  29. data/spec/functional/behavior_spec.rb +12 -33
  30. data/spec/functional/cached_thread_pool_spec.rb +112 -0
  31. data/spec/functional/concurrency_spec.rb +55 -0
  32. data/spec/functional/event_machine_defer_proxy_spec.rb +246 -0
  33. data/spec/functional/event_spec.rb +114 -0
  34. data/spec/functional/fixed_thread_pool_spec.rb +84 -0
  35. data/spec/functional/future_spec.rb +115 -0
  36. data/spec/functional/obligation_shared.rb +121 -0
  37. data/spec/functional/pattern_matching_spec.rb +10 -8
  38. data/spec/functional/promise_spec.rb +310 -0
  39. data/spec/functional/thread_pool_shared.rb +209 -0
  40. data/spec/functional/utilities_spec.rb +149 -0
  41. data/spec/spec_helper.rb +2 -0
  42. metadata +55 -5
data/README.md CHANGED
@@ -1,79 +1,77 @@
1
- # Functional Ruby [![Build Status](https://secure.travis-ci.org/jdantonio/functional-ruby.png)](http://travis-ci.org/jdantonio/functional-ruby?branch=master) [![Dependency Status](https://gemnasium.com/jdantonio/functional-ruby.png)](https://gemnasium.com/jdantonio/functional-ruby)
1
+ # Functional Ruby [![Build Status](https://secure.travis-ci.org/jdantonio/functional-ruby.png)](https://travis-ci.org/jdantonio/functional-ruby?branch=master) [![Dependency Status](https://gemnasium.com/jdantonio/functional-ruby.png)](https://gemnasium.com/jdantonio/functional-ruby)
2
2
 
3
- A gem for adding Erlang and Clojure inspired functional programming tools to Ruby.
3
+ A gem for adding Erlang, Clojure, and Go inspired concurrency and functional programming tools to Ruby.
4
4
 
5
5
  The project is hosted on the following sites:
6
6
 
7
7
  * [RubyGems project page](https://rubygems.org/gems/functional-ruby)
8
8
  * [Source code on GitHub](https://github.com/jdantonio/functional-ruby)
9
+ * [YARD documentation on RubyDoc.info](http://rubydoc.info/github/jdantonio/functional-ruby/master/frames)
9
10
  * [Continuous integration on Travis-CI](https://travis-ci.org/jdantonio/functional-ruby)
10
11
  * [Dependency tracking on Gemnasium](https://gemnasium.com/jdantonio/functional-ruby)
11
12
  * [Follow me on Twitter](https://twitter.com/jerrydantonio)
12
13
 
13
14
  ## Introduction
14
15
 
15
- [Ruby](http://www.ruby-lang.org/en/) is my favorite programming by far. As much as I love
16
- Ruby I've always been a little disappointed that Ruby doesn't support function overloading.
17
- Function overloading tends to reduce branching and keep function signatures simpler.
18
- No sweat, I learned to do without. Then I started programming in [Erlang](http://www.erlang.org/)...
19
-
20
- I've really started to enjoy working in Erlang. Erlang is good at all the things Ruby is bad
21
- at and vice versa. Together, Ruby and Erlang make me happy. My favorite Erlang feature is,
22
- without question, [pattern matching](http://learnyousomeerlang.com/syntax-in-functions#pattern-matching).
23
- Pattern matching is like function overloading cranked to 11. So one day I was musing on Twitter
24
- that I'd like to see Erlang-stype pattern matching in Ruby and one of my friends responded "Build it!"
25
- So I did. And here it is.
26
-
27
- For fun I threw in Erlang's sparsely documented [-behaviour](http://www.erlang.org/doc/design_principles/gen_server_concepts.html)
28
- functionality plus a few other functions and constants I find useful. Eventually I realized I was
29
- building something much more than just Erlang's pattern matching. I was creating a broader library
30
- for helping programmers write Ruby code in a functional style. So I changed the name of the gem
31
- and kept on trucking.
16
+ Three things I love are [Ruby](http://www.ruby-lang.org/en/),
17
+ [functional](https://en.wikipedia.org/wiki/Functional_programming)
18
+ [programming](http://c2.com/cgi/wiki?FunctionalProgramming) and
19
+ [concurrency](http://www.amazon.com/s/ref=nb_sb_noss_1?url=search-alias%3Dstripbooks&field-keywords=concurrent%20programming).
20
+ Sadly, the first is generally not associated with the other two. First, I reject the
21
+ assertion that Ruby is an object-oriented language. It's certainly object-based, since
22
+ everything is an object, but entire large-scale programs can be built without ever
23
+ defining a single class. Ruby is a true multi-paradigm language and easily supports
24
+ many advanced functional techniques. As to concurrency, Ruby's bad reputation is
25
+ well earned, but recent versions of Ruby have made significan improvements in that
26
+ area. Ruby 2.0 is now a [relevant](https://blog.heroku.com/archives/2013/6/17/ruby-2-default-new-aps)
27
+ platform for concurrent applications.
28
+
29
+ This gem is my small and humble attempt to help Ruby reach its full potential as
30
+ a highly performant, functional, concurrent programming language.
32
31
 
33
32
  ### Goals
34
33
 
35
- * Stay true to the spirit of Erlang pattern matching, if not the semantics
34
+ My history with high-performance, highly-concurrent programming goes back to my days with C/C++.
35
+ I have the same scars as everyone else doing that kind of work with those languages.
36
+ I'm fascinated by modern concurrency patterns like [Actors](http://en.wikipedia.org/wiki/Actor_model),
37
+ [Agents](http://doc.akka.io/docs/akka/snapshot/java/agents.html), and
38
+ [Promises](http://promises-aplus.github.io/promises-spec/). I'm equally fascinated by languages
39
+ with strong concurrency support like [Erlang](http://www.erlang.org/doc/getting_started/conc_prog.html),
40
+ [Go](http://golang.org/doc/articles/concurrency_patterns.html), and
41
+ [Clojure](http://clojure.org/concurrent_programming) (I program with Erlang at work).
42
+ My goal is to implement those patterns in Ruby. Specifically:
43
+
44
+ * Stay true to the spirit of the languages providing inspiration
45
+ * But implement in a way that makes sense for Ruby
36
46
  * Keep the semantics as idiomatic Ruby as possible
37
47
  * Support features that make sense in Ruby
38
- * Exclude features that only make sense in Erlang
39
- * Avoid using *method_missing*
48
+ * Exclude features that don't make sense in Ruby
40
49
  * Keep everything small
41
50
  * Be as fast as reasonably possible
42
51
 
43
- ### Features
44
-
45
- * Pattern matching for instance methods.
46
- * Pattern matching for object constructors.
47
- * Parameter count matching
48
- * Matching against primitive values
49
- * Matching by class/datatype
50
- * Matching against specific key/vaue pairs in hashes
51
- * Matching against the presence of keys within hashes
52
- * Implicit hash for last parameter
53
- * Variable-length parameter lists
54
- * Guard clauses
55
- * Recursive calls to other pattern matches
56
- * Recursive calls to superclass pattern matches
57
- * Recursive calls to superclass methods
58
- * Dispatching to superclass methods when no match is found
59
- * Reasonable error messages when no match is found
60
-
61
- ### For good -behavior(timeoff).
62
-
63
- One of Ruby's greatest strengths is [duck typing](http://rubylearning.com/satishtalim/duck_typing.html).
64
- Usually this is awesome and I'm happy to not have to deal with static typing and the compiler. Usually.
65
- The problem with duck typing is that is is impossible in Ruby to enforce an interface definition.
66
- I would never advocate turning Ruby into the cesspool complex object creation that Java has
67
- unfortunately become, but occasionally it would be nice to make sure a class implements a set of
68
- required methods. Enter Erlang's [-behavior](http://metajack.im/2008/10/29/custom-behaviors-in-erlang/)
69
- keyword. Basically, you define a `behavior_info` then drop a `behavior` call within a class.
70
- Forget to implement a required method and Ruby will let you know. See the examples below for details.
71
-
72
- ## Supported Ruby versions
73
-
74
- MRI 1.9.x and above. Anything else and your mileage may vary.
75
-
76
- ## Install
52
+ ## Features (and Documentation)
53
+
54
+ Several features from Erlang, Co, Clojure, and JavaScript have been implemented this far:
55
+
56
+ * Function overloading with Erlang-style [Pattern Matching](https://github.com/jdantonio/functional-ruby/blob/master/md/pattern_matching.md)
57
+ * Interface specifications with Erlang-style [Behavior](https://github.com/jdantonio/functional-ruby/blob/master/md/behavior.md)
58
+ * Chained asynchronous operations inspried by JavaScript [Promises](https://github.com/jdantonio/functional-ruby/blob/master/md/promise.md)
59
+ * Additional Clojure, Go, and Erlang inspired [Concurrency](https://github.com/jdantonio/functional-ruby/blob/master/md/concurrency.md)
60
+ * Several useful functional [Utilities](https://github.com/jdantonio/functional-ruby/blob/master/md/utilities.md)
61
+
62
+ ### Is it any good?
63
+
64
+ [Yes](http://news.ycombinator.com/item?id=3067434)
65
+
66
+ ### Supported Ruby versions
67
+
68
+ MRI 1.9.2, 1.9.3, and 2.0. This library is pure Ruby and has no gem dependencies. It should be
69
+ fully compatible with any Ruby interpreter that is 1.9.x compliant. I simply don't know enough
70
+ about JRuby, Rubinius, or the others to fully support them. I can promise good karma and
71
+ attribution on this page to anyone wishing to take responsibility for verifying compaitibility
72
+ with any Ruby other than MRI.
73
+
74
+ ### Install
77
75
 
78
76
  ```shell
79
77
  gem install functional-ruby
@@ -93,8 +91,10 @@ that not all users may want, several `require` options are available:
93
91
  ```ruby
94
92
  require 'functional/behavior'
95
93
  require 'functional/behaviour' # alternate spelling
94
+ require 'functional/concurrency'
96
95
  require 'functional/pattern_matching'
97
- require 'functional/core'
96
+ require 'functional/promise'
97
+ require 'functional/utilities'
98
98
  ```
99
99
 
100
100
  If you want everything you can do that, too:
@@ -103,18 +103,14 @@ If you want everything you can do that, too:
103
103
  require 'functional/all'
104
104
  ```
105
105
 
106
- ## PatternMatching
106
+ ## Examples
107
107
 
108
- First, familiarize yourself with Erlang [pattern matching](http://learnyousomeerlang.com/syntax-in-functions#pattern-matching).
109
- This gem may not make much sense if you don't understand how Erlang dispatches functions.
108
+ For complete examples, see the specific documentation linked above. Below are a
109
+ few examples to whet your appetite.
110
110
 
111
- In the Ruby class file where you want to use pattern matching, require the *functional-ruby* gem:
111
+ ### Pattern Matching (Erlang)
112
112
 
113
- ```ruby
114
- require 'functional/pattern_matching'
115
- ```
116
-
117
- Then include `PatternMatching` in your class:
113
+ Documentation: [Pattern Matching](https://github.com/jdantonio/functional-ruby/blob/master/md/pattern_matching.md)
118
114
 
119
115
  ```ruby
120
116
  require 'functional/pattern_matching'
@@ -122,548 +118,144 @@ require 'functional/pattern_matching'
122
118
  class Foo
123
119
  include PatternMatching
124
120
 
125
- ...
126
-
127
- end
128
- ```
129
-
130
- You can then define functions with `defn` instead of the normal *def* statement.
131
- The syntax for `defn` is:
132
-
133
- ```ruby
134
- defn(:symbol_name_of_function, zero, or, more, parameters) { |block, arguments|
135
- # code to execute
136
- }
137
- ```
138
- You can then call your new function just like any other:
139
-
140
- ```ruby
141
- require 'functional/pattern_matching'
142
-
143
- class Foo
144
- include PatternMatching
121
+ defn(:greet, :male) {
122
+ puts "Hello, sir!"
123
+ }
145
124
 
146
- defn(:hello) {
147
- puts "Hello, World!"
125
+ defn(:greet, :female) {
126
+ puts "Hello, ma'am!"
148
127
  }
149
128
  end
150
129
 
151
130
  foo = Foo.new
152
- foo.hello #=> "Hello, World!"
153
- ```
154
-
155
- Patterns to match against are included in the parameter list:
156
-
157
- ```ruby
158
- defn(:greet, :male) {
159
- puts "Hello, sir!"
160
- }
161
-
162
- defn(:greet, :female) {
163
- puts "Hello, ma'am!"
164
- }
165
-
166
- ...
167
-
168
- foo.hello(:male) #=> "Hello, sir!"
169
- foo.hello(:female) #=> "Hello, ma'am!"
170
- ```
171
-
172
- If a particular method call can not be matched a *NoMethodError* is thrown with
173
- a reasonably helpful error message:
174
-
175
- ```ruby
176
- foo.greet(:unknown) #=> NoMethodError: no method `greet` matching [:unknown] found for class Foo
177
- foo.greet #=> NoMethodError: no method `greet` matching [] found for class Foo
178
- ```
179
-
180
- Parameters that are expected to exist but that can take any value are considered
181
- *unbound* parameters. Unbound parameters are specified by the `_` underscore
182
- character or `UNBOUND`:
183
-
184
- ```ruby
185
- defn(:greet, _) do |name|
186
- "Hello, #{name}!"
187
- end
188
-
189
- defn(:greet, UNBOUND, UNBOUND) do |first, last|
190
- "Hello, #{first} #{last}!"
191
- end
192
-
193
- ...
194
-
195
- foo.greet('Jerry') #=> "Hello, Jerry!"
131
+ foo.greet(:male) #=> "Hello, sir!"
132
+ foo.greet(:female) #=> "Hello, ma'am!"
196
133
  ```
197
134
 
198
- All unbound parameters will be passed to the block in the order they are specified in the definition:
199
-
200
- ```ruby
201
- defn(:greet, _, _) do |first, last|
202
- "Hello, #{first} #{last}!"
203
- end
204
-
205
- ...
206
-
207
- foo.greet('Jerry', "D'Antonio") #=> "Hello, Jerry D'Antonio!"
208
- ```
209
-
210
- If for some reason you don't care about one or more unbound parameters within
211
- the block you can use the `_` underscore character in the block parameters list
212
- as well:
213
-
214
- ```ruby
215
- defn(:greet, _, _, _) do |first, _, last|
216
- "Hello, #{first} #{last}!"
217
- end
218
-
219
- ...
220
-
221
- foo.greet('Jerry', "I'm not going to tell you my middle name!", "D'Antonio") #=> "Hello, Jerry D'Antonio!"
222
- ```
135
+ ### Behavior (Erlang)
223
136
 
224
- Hash parameters can match against specific keys and either bound or unbound parameters. This allows for
225
- function dispatch by hash parameters without having to dig through the hash:
137
+ Documentation: [Behavior](https://github.com/jdantonio/functional-ruby/blob/master/md/behavior.md)
226
138
 
227
139
  ```ruby
228
- defn(:hashable, {foo: :bar}) { |opts|
229
- :foo_bar
230
- }
231
- defn(:hashable, {foo: _}) { |f|
232
- f
233
- }
234
-
235
- ...
236
-
237
- foo.hashable({foo: :bar}) #=> :foo_bar
238
- foo.hashable({foo: :baz}) #=> :baz
239
- ```
240
-
241
- The Ruby idiom of the final parameter being a hash is also supported:
242
-
243
- ```ruby
244
- defn(:options, _) { |opts|
245
- opts
246
- }
247
-
248
- ...
249
-
250
- foo.options(bar: :baz, one: 1, many: 2)
251
- ```
252
-
253
- As is the Ruby idiom of variable-length argument lists. The constant `ALL` as the last parameter
254
- will match one or more arguments and pass them to the block as an array:
255
-
256
- ```ruby
257
- defn(:baz, Integer, ALL) { |int, args|
258
- [int, args]
259
- }
260
- defn(:baz, ALL) { |args|
261
- args
262
- }
263
- ```
264
-
265
- Superclass polymorphism is supported as well. If an object cannot match a method
266
- signature it will defer to the parent class:
267
-
268
- ```ruby
269
- class Bar
270
- def greet
271
- return 'Hello, World!'
272
- end
273
- end
274
-
275
- class Foo < Bar
276
- include PatternMatching
277
-
278
- defn(:greet, _) do |name|
279
- "Hello, #{name}!"
280
- end
281
- end
282
-
283
- ...
284
-
285
- foo.greet('Jerry') #=> "Hello, Jerry!"
286
- foo.greet #=> "Hello, World!"
287
- ```
288
-
289
- Guard clauses in Erlang are defined with `when` clauses between the parameter list and the function body.
290
- In Ruby, guard clauses are defined by chaining a call to `when` onto the the `defn` call and passing
291
- a block. If the guard clause evaluates to true then the function will match. If the guard evaluates
292
- to false the function will not match and pattern matching will continue:
293
-
294
- Erlang:
295
-
296
- ```erlang
297
- old_enough(X) when X >= 16 -> true;
298
- old_enough(_) -> false.
299
- ```
300
-
301
- Ruby:
302
-
303
- ```ruby
304
- defn(:old_enough, _){ true }.when{|x| x >= 16 }
305
- defn(:old_enough, _){ false }
306
- ```
307
-
308
- ### Order Matters
309
-
310
- As with Erlang, the order of pattern matches is significant. Patterns will be matched
311
- *in the order declared* and the first match will be used. If a particular function call
312
- can be matched by more than one pattern, the *first matched pattern* will be used. It
313
- is the programmer's responsibility to ensure patterns are declared in the correct order.
314
-
315
- ### Blocks and Procs and Lambdas, oh my!
316
-
317
- When using this gem it is critical to remember that `defn` takes a block and
318
- that blocks in Ruby have special rules. There are [plenty](https://www.google.com/search?q=ruby+block+proc+lambda)
319
- of good tutorials on the web explaining [blocks](http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/)
320
- and [Procs](https://coderwall.com/p/_-_mha) and [lambdas](http://railsguru.org/2010/03/learn-ruby-procs-blocks-lambda/)
321
- in Ruby. Please read them. Please don't submit a bug report if you use a
322
- `return` statement within your `defn` and your code blows up with a
323
- [LocalJumpError](http://ruby-doc.org/core-2.0/LocalJumpError.html).
324
-
325
- ### Examples
326
-
327
- For more examples see the integration tests in *spec/integration_spec.rb*.
328
-
329
- #### Simple Functions
330
-
331
- 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/).
332
-
333
- Erlang:
334
-
335
- ```erlang
336
- greet(male, Name) ->
337
- io:format("Hello, Mr. ~s!", [Name]);
338
- greet(female, Name) ->
339
- io:format("Hello, Mrs. ~s!", [Name]);
340
- greet(_, Name) ->
341
- io:format("Hello, ~s!", [Name]).
342
- ```
343
-
344
- Ruby:
140
+ require 'functional/behavior'
345
141
 
346
- ```ruby
347
- require 'functional/pattern_matching'
142
+ behaviour_info(:gen_foo, foo: 0, bar: 1)
348
143
 
349
144
  class Foo
350
- include PatternMatching
145
+ behavior(:gen_foo)
351
146
 
352
- defn(:greet, _) do |name|
353
- "Hello, #{name}!"
147
+ def foo
148
+ return 'foo/0'
354
149
  end
355
150
 
356
- defn(:greet, :male, _) { |name|
357
- "Hello, Mr. #{name}!"
358
- }
359
- defn(:greet, :female, _) { |name|
360
- "Hello, Ms. #{name}!"
361
- }
362
- defn(:greet, _, _) { |_, name|
363
- "Hello, #{name}!"
364
- }
365
- end
366
- ```
367
-
368
- #### Simple Functions with Overloading
369
-
370
- 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/).
371
-
372
- Erlang:
373
-
374
- ```erlang
375
- greet(Name) ->
376
- io:format("Hello, ~s!", [Name]).
377
-
378
- greet(male, Name) ->
379
- io:format("Hello, Mr. ~s!", [Name]);
380
- greet(female, Name) ->
381
- io:format("Hello, Mrs. ~s!", [Name]);
382
- greet(_, Name) ->
383
- io:format("Hello, ~s!", [Name]).
384
- ```
385
-
386
- Ruby:
387
-
388
- ```ruby
389
- require 'functional/pattern_matching'
390
-
391
- class Foo
392
- include PatternMatching
393
-
394
- defn(:greet, _) do |name|
395
- "Hello, #{name}!"
151
+ def bar(one, &block)
152
+ return 'bar/1'
396
153
  end
397
-
398
- defn(:greet, :male, _) { |name|
399
- "Hello, Mr. #{name}!"
400
- }
401
- defn(:greet, :female, _) { |name|
402
- "Hello, Ms. #{name}!"
403
- }
404
- defn(:greet, nil, _) { |name|
405
- "Goodbye, #{name}!"
406
- }
407
- defn(:greet, _, _) { |_, name|
408
- "Hello, #{name}!"
409
- }
410
- end
411
- ```
412
-
413
- #### Constructor Overloading
414
-
415
- ```ruby
416
- require 'functional/pattern_matching'
417
-
418
- class Foo
419
- include PatternMatching
420
-
421
- defn(:initialize) { @name = 'baz' }
422
- defn(:initialize, _) {|name| @name = name.to_s }
423
154
  end
424
- ```
425
-
426
- #### Matching by Class/Datatype
427
-
428
- ```ruby
429
- require 'functional/pattern_matching'
430
-
431
- class Foo
432
- include PatternMatching
433
-
434
- defn(:concat, Integer, Integer) { |first, second|
435
- first + second
436
- }
437
- defn(:concat, Integer, String) { |first, second|
438
- "#{first} #{second}"
439
- }
440
- defn(:concat, String, String) { |first, second|
441
- first + second
442
- }
443
- defn(:concat, Integer, _) { |first, second|
444
- first + second.to_i
445
- }
446
- end
447
- ```
448
155
 
449
- #### Matching a Hash Parameter
450
-
451
- ```ruby
452
- require 'functional/pattern_matching'
453
-
454
- class Foo
455
- include PatternMatching
456
-
457
- defn(:hashable, {foo: :bar}) { |opts|
458
- # matches any hash with key :foo and value :bar
459
- :foo_bar
460
- }
461
- defn(:hashable, {foo: _, bar: _}) { |f, b|
462
- # matches any hash with keys :foo and :bar
463
- # passes the values associated with those keys to the block
464
- [f, b]
465
- }
466
- defn(:hashable, {foo: _}) { |f|
467
- # matches any hash with key :foo
468
- # passes the value associated with that key to the block
469
- # must appear AFTER the prior match or it will override that one
470
- f
471
- }
472
- defn(:hashable, {}) { ||
473
- # matches an empty hash
474
- :empty
475
- }
476
- defn(:hashable, _) { |opts|
477
- # matches any hash (or any other value)
478
- opts
479
- }
480
- end
481
-
482
- ...
483
-
484
- foo.hashable({foo: :bar}) #=> :foo_bar
485
- foo.hashable({foo: :baz}) #=> :baz
486
- foo.hashable({foo: 1, bar: 2}) #=> [1, 2]
487
- foo.hashable({foo: 1, baz: 2}) #=> 1
488
- foo.hashable({bar: :baz}) #=> {bar: :baz}
489
- foo.hashable({}) #=> :empty
490
- ```
491
-
492
- #### Variable Length Argument Lists with ALL
156
+ foo = Foo.new
493
157
 
494
- ```ruby
495
- defn(:all, :one, ALL) { |args|
496
- args
497
- }
498
- defn(:all, :one, Integer, ALL) { |int, args|
499
- [int, args]
500
- }
501
- defn(:all, 1, _, ALL) { |var, args|
502
- [var, args]
503
- }
504
- defn(:all, ALL) { | args|
505
- args
506
- }
507
-
508
- ...
509
-
510
- foo.all(:one, 'a', 'bee', :see) #=> ['a', 'bee', :see]
511
- foo.all(:one, 1, 'bee', :see) #=> [1, 'bee', :see]
512
- foo.all(1, 'a', 'bee', :see) #=> ['a', ['bee', :see]]
513
- foo.all('a', 'bee', :see) #=> ['a', 'bee', :see]
514
- foo.all() #=> NoMethodError: no method `all` matching [] found for class Foo
158
+ foo.behaves_as? :gen_foo #=> true
159
+ foo.behaves_as?(:bogus) #=> false
160
+ 'foo'.behaves_as? :gen_foo #=> false
515
161
  ```
516
162
 
517
- #### Guard Clauses
518
-
519
- These examples are based on [Syntax in defnctions: Pattern Matching](http://learnyousomeerlang.com/syntax-in-defnctions)
520
- in [Learn You Some Erlang for Great Good!](http://learnyousomeerlang.com/).
521
-
522
- Erlang:
523
-
524
- ```erlang
525
- old_enough(X) when X >= 16 -> true;
526
- old_enough(_) -> false.
527
-
528
- right_age(X) when X >= 16, X =< 104 ->
529
- true;
530
- right_age(_) ->
531
- false.
532
-
533
- wrong_age(X) when X < 16; X > 104 ->
534
- true;
535
- wrong_age(_) ->
536
- false.
537
- ```
163
+ ### Goroutine (Go)
538
164
 
539
165
  ```ruby
540
- defn(:old_enough, _){ true }.when{|x| x >= 16 }
541
- defn(:old_enough, _){ false }
542
-
543
- defn(:right_age, _) {
544
- true
545
- }.when{|x| x >= 16 && x <= 104 }
546
-
547
- defn(:right_age, _) {
548
- false
549
- }
550
-
551
- defn(:wrong_age, _) {
552
- false
553
- }.when{|x| x < 16 || x > 104 }
166
+ require 'functional/concurrency'
554
167
 
555
- defn(:wrong_age, _) {
556
- true
557
- }
168
+ @expected = nil
169
+ go(1, 2, 3){|a, b, c| @expected = [c, b, a] }
170
+ sleep(0.1)
171
+ @expected #=> [3, 2, 1]
558
172
  ```
559
173
 
560
- ## Behavior
561
-
562
- The `behavior` functionality is not imported by default. It requires a separate `require` statement:
174
+ ### Agent (Clojure)
563
175
 
564
176
  ```ruby
565
- require 'behavior'
566
-
567
- # -or-
568
-
569
- require 'behaviour'
570
- ```
177
+ require 'functional/agent'
178
+ # or
179
+ require 'functional/concurrency'
571
180
 
572
- Next, declare a behavior using the `behavior_info` function (this function should sit outside
573
- of any module/class definition, but will probably work regardless). The first parameter to
574
- `behavior_info` (or `behaviour_info`) is a symbol name for the behavior. The remaining parameter
575
- is a hash of function names and their arity:
181
+ score = agent(10)
182
+ score.value #=> 10
576
183
 
577
- ```ruby
578
- behaviour_info(:gen_foo, foo: 0, bar: 1, baz: 2)
579
-
580
- # -or (for the Java/C# crowd)
184
+ score << proc{|current| current + 100 }
185
+ sleep(0.1)
186
+ score.value #=> 110
581
187
 
582
- interface(:gen_foo, foo: 0, bar: 1, baz: 2)
188
+ score << proc{|current| current * 2 }
189
+ sleep(0.1)
190
+ deref score #=> 220
583
191
 
192
+ score << proc{|current| current - 50 }
193
+ sleep(0.1)
194
+ score.value #=> 170
584
195
  ```
585
196
 
586
- Each function name can be listed only once and the arity must follow the rules of the
587
- [Method#arity](http://ruby-doc.org/core-1.9.3/Method.html#method-i-arity) function.
588
- Though not explicitly documented, block arguments do not count toward a method's arity.
589
- methods defined using this gem's `defn` function will always have an arity of -1,
590
- regardless of how many overloads are defined.
591
-
592
- To enforce a behavior on a class simply call the `behavior` function within the class,
593
- passing the name of the desired behavior:
197
+ ### Future (Clojure)
594
198
 
595
199
  ```ruby
596
- class Foo
597
- behavior(:gen_foo)
598
- ...
599
- end
600
-
601
- # or use the idiomatic Erlang spelling
602
- class Bar
603
- behaviour(:gen_foo)
604
- ...
605
- end
200
+ require 'functional/future'
201
+ # or
202
+ require 'functional/concurrency'
606
203
 
607
- # or use the idiomatic Rails syntax
608
- class Baz
609
- behaves_as :gen_foo
610
- ...
611
- end
204
+ count = future{ sleep(1); 10 }
205
+ count.state #=> :pending
206
+ # do stuff...
207
+ count.value #=> 10 (after blocking)
208
+ deref count #=> 10
612
209
  ```
613
210
 
614
- Make sure you the implement the required methods in your class. If you don't, Ruby will
615
- raise an exception when you try to create an object from the class:
211
+ ### Promise (JavaScript)
616
212
 
617
- ```ruby
618
- Baz.new #=> ArgumentError: undefined callback functions in Baz (behavior 'gen_foo')
619
- ```
620
-
621
- As an added bonus, Ruby [Object](http://ruby-doc.org/core-1.9.3/Object.html) will be
622
- monkey-patched with a `behaves_as?` predicate method.
623
-
624
- A complete example:
213
+ * [Promises/A](http://wiki.commonjs.org/wiki/Promises/A)
214
+ * [Promises/A+](http://promises-aplus.github.io/promises-spec/)
625
215
 
626
216
  ```ruby
627
- behaviour_info(:gen_foo, foo: 0, bar: 1, baz: 2, boom: -1, bam: :any)
628
-
629
- class Foo
630
- behavior(:gen_foo)
631
-
632
- def foo
633
- return 'foo/0'
634
- end
635
-
636
- def bar(one, &block)
637
- return 'bar/1'
638
- end
639
-
640
- def baz(one, two)
641
- return 'baz/2'
642
- end
217
+ require 'functional/promise'
218
+ # or
219
+ require 'functional/concurrency'
643
220
 
644
- def boom(*args)
645
- return 'boom/-1'
646
- end
647
-
648
- def bam
649
- return 'bam!'
650
- end
651
- end
652
-
653
- foo = Foo.new
654
-
655
- foo.behaves_as? :gen_foo #=> true
656
- foo.behaves_as?(:bogus) #=> false
657
- 'foo'.behaves_as? :gen_foo #=> false
221
+ p = promise("Jerry", "D'Antonio"){|a, b| "#{a} #{b}" }.
222
+ then{|result| "Hello #{result}." }.
223
+ rescue(StandardError){|ex| puts "Boom!" }.
224
+ then{|result| "#{result} Would you like to play a game?"}
225
+ sleep(1)
226
+ p.value #=> "Hello Jerry D'Antonio. Would you like to play a game?"
658
227
  ```
659
228
 
660
- ## Utility Functions
661
-
662
- Convenience functions are not imported by default. It require a separate `require` statement:
229
+ ### Thread Pools
663
230
 
664
231
  ```ruby
665
- require 'functional/core'
666
- ```
232
+ require 'functional/fixed_thread_pool'
233
+ require 'functional/cached_thread_pool'
234
+ # or
235
+ require 'functional/concurrency'
236
+
237
+ pool = Functional::FixedThreadPool.new(10)
238
+ @expected = 0
239
+ pool.post{ sleep(0.5); @expected += 100 }
240
+ pool.post{ sleep(0.5); @expected += 100 }
241
+ pool.post{ sleep(0.5); @expected += 100 }
242
+ @expected #=> nil
243
+ sleep(1)
244
+ @expected #=> 300
245
+
246
+ pool = Functional::CachedThreadPool.new
247
+ @expected = 0
248
+ pool << proc{ sleep(0.5); @expected += 10 }
249
+ pool << proc{ sleep(0.5); @expected += 10 }
250
+ pool << proc{ sleep(0.5); @expected += 10 }
251
+ @expected #=> 0
252
+ sleep(1)
253
+ @expected #=> 30
254
+ ```
255
+
256
+ ### Utilities
257
+
258
+ Documentation: [Utilities](https://github.com/jdantonio/functional-ruby/blob/master/md/utilities.md)
667
259
 
668
260
  ```ruby
669
261
  Infinity #=> Infinity
@@ -678,9 +270,9 @@ pp_s [1,2,3,4] #=> "[1, 2, 3, 4]\n" props to Rha7
678
270
 
679
271
  delta(-1, 1) #=> 2
680
272
  delta({count: -1}, {count: 1}){|item| item[:count]} #=> 2
681
- ```
682
273
 
683
- This gives you access to a few constants and functions:
274
+ # And many more!
275
+ ```
684
276
 
685
277
  ## Copyright
686
278