functional-ruby 0.5.0 → 0.6.0

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