pure 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. data/CHANGES.rdoc +7 -0
  2. data/MANIFEST +44 -20
  3. data/README.rdoc +553 -16
  4. data/Rakefile +25 -2
  5. data/devel/jumpstart.rb +606 -253
  6. data/install.rb +1 -2
  7. data/lib/pure.rb +38 -16
  8. data/lib/pure/bundled_parsers.rb +4 -0
  9. data/lib/pure/bundled_plugin.rb +49 -0
  10. data/lib/pure/compiler/ruby_parser.rb +63 -0
  11. data/lib/pure/delegate.rb +16 -0
  12. data/lib/pure/driver.rb +33 -0
  13. data/lib/pure/dsl.rb +2 -0
  14. data/lib/pure/dsl_definition.rb +11 -0
  15. data/lib/pure/error.rb +89 -0
  16. data/lib/pure/extracted_functions.rb +11 -0
  17. data/lib/pure/extractor.rb +59 -0
  18. data/lib/pure/names.rb +9 -0
  19. data/lib/pure/native_worker.rb +27 -0
  20. data/lib/pure/parser/impl/base_parser.rb +21 -0
  21. data/lib/pure/parser/impl/internal.rb +31 -0
  22. data/lib/pure/parser/impl/ripper.rb +96 -0
  23. data/lib/pure/parser/impl/ruby_parser.rb +77 -0
  24. data/lib/pure/parser/internal.rb +4 -0
  25. data/lib/pure/parser/ripper.rb +2 -0
  26. data/lib/pure/parser/ruby_parser.rb +2 -0
  27. data/lib/pure/pure.rb +32 -0
  28. data/lib/pure/pure_module.rb +141 -0
  29. data/lib/pure/util.rb +15 -0
  30. data/lib/pure/version.rb +4 -0
  31. data/spec/compiler_ruby_parser_spec.rb +79 -0
  32. data/spec/compute_overrides_spec.rb +99 -0
  33. data/spec/compute_spec.rb +86 -0
  34. data/spec/compute_thread_spec.rb +29 -0
  35. data/spec/compute_timed_spec.rb +40 -0
  36. data/spec/delegate_spec.rb +141 -0
  37. data/spec/fstat_example.rb +26 -0
  38. data/spec/parser_sexp_spec.rb +100 -0
  39. data/spec/parser_spec.rb +18 -31
  40. data/spec/pure_combine_spec.rb +77 -0
  41. data/spec/pure_def_spec.rb +186 -0
  42. data/spec/pure_define_method_spec.rb +24 -0
  43. data/spec/pure_eval_spec.rb +18 -0
  44. data/spec/pure_fun_spec.rb +243 -0
  45. data/spec/pure_nested_spec.rb +35 -0
  46. data/spec/pure_parser_spec.rb +50 -0
  47. data/spec/pure_spec.rb +81 -0
  48. data/spec/pure_spec_base.rb +106 -0
  49. data/spec/pure_splat_spec.rb +18 -0
  50. data/spec/pure_two_defs_spec.rb +20 -0
  51. data/spec/pure_worker_spec.rb +33 -0
  52. data/spec/readme_spec.rb +36 -32
  53. data/spec/splat_spec.rb +12 -11
  54. data/spec/worker_spec.rb +89 -0
  55. metadata +157 -41
  56. data/devel/jumpstart/lazy_attribute.rb +0 -38
  57. data/devel/jumpstart/ruby.rb +0 -44
  58. data/devel/jumpstart/simple_installer.rb +0 -85
  59. data/lib/pure/pure_private/creator.rb +0 -27
  60. data/lib/pure/pure_private/driver.rb +0 -48
  61. data/lib/pure/pure_private/error.rb +0 -32
  62. data/lib/pure/pure_private/extractor.rb +0 -79
  63. data/lib/pure/pure_private/extractor_ripper.rb +0 -95
  64. data/lib/pure/pure_private/extractor_ruby_parser.rb +0 -47
  65. data/lib/pure/pure_private/function_database.rb +0 -10
  66. data/lib/pure/pure_private/singleton_features.rb +0 -67
  67. data/lib/pure/pure_private/util.rb +0 -23
  68. data/spec/basic_spec.rb +0 -38
  69. data/spec/combine_spec.rb +0 -62
  70. data/spec/common.rb +0 -44
  71. data/spec/error_spec.rb +0 -146
  72. data/spec/fun_spec.rb +0 -122
  73. data/spec/lazy_spec.rb +0 -22
  74. data/spec/subseqent_spec.rb +0 -42
  75. data/spec/timed_spec.rb +0 -30
@@ -1,6 +1,13 @@
1
1
 
2
2
  = Pure ChangeLog
3
3
 
4
+ == Version 0.2.0
5
+
6
+ * more docs
7
+ * better handling of default parse engine
8
+ * rspec-1.2 changes
9
+ * internal cleanup
10
+
4
11
  == Version 0.1.0
5
12
 
6
13
  * Initial release.
data/MANIFEST CHANGED
@@ -3,28 +3,52 @@ MANIFEST
3
3
  README.rdoc
4
4
  Rakefile
5
5
  devel/jumpstart.rb
6
- devel/jumpstart/lazy_attribute.rb
7
- devel/jumpstart/ruby.rb
8
- devel/jumpstart/simple_installer.rb
9
6
  install.rb
10
7
  lib/pure.rb
11
- lib/pure/pure_private/creator.rb
12
- lib/pure/pure_private/driver.rb
13
- lib/pure/pure_private/error.rb
14
- lib/pure/pure_private/extractor.rb
15
- lib/pure/pure_private/extractor_ripper.rb
16
- lib/pure/pure_private/extractor_ruby_parser.rb
17
- lib/pure/pure_private/function_database.rb
18
- lib/pure/pure_private/singleton_features.rb
19
- lib/pure/pure_private/util.rb
20
- spec/basic_spec.rb
21
- spec/combine_spec.rb
22
- spec/common.rb
23
- spec/error_spec.rb
24
- spec/fun_spec.rb
25
- spec/lazy_spec.rb
8
+ lib/pure/bundled_parsers.rb
9
+ lib/pure/bundled_plugin.rb
10
+ lib/pure/compiler/ruby_parser.rb
11
+ lib/pure/delegate.rb
12
+ lib/pure/driver.rb
13
+ lib/pure/dsl.rb
14
+ lib/pure/dsl_definition.rb
15
+ lib/pure/error.rb
16
+ lib/pure/extracted_functions.rb
17
+ lib/pure/extractor.rb
18
+ lib/pure/names.rb
19
+ lib/pure/native_worker.rb
20
+ lib/pure/parser/impl/base_parser.rb
21
+ lib/pure/parser/impl/internal.rb
22
+ lib/pure/parser/impl/ripper.rb
23
+ lib/pure/parser/impl/ruby_parser.rb
24
+ lib/pure/parser/internal.rb
25
+ lib/pure/parser/ripper.rb
26
+ lib/pure/parser/ruby_parser.rb
27
+ lib/pure/pure.rb
28
+ lib/pure/pure_module.rb
29
+ lib/pure/util.rb
30
+ lib/pure/version.rb
31
+ spec/compiler_ruby_parser_spec.rb
32
+ spec/compute_overrides_spec.rb
33
+ spec/compute_spec.rb
34
+ spec/compute_thread_spec.rb
35
+ spec/compute_timed_spec.rb
36
+ spec/delegate_spec.rb
37
+ spec/fstat_example.rb
38
+ spec/parser_sexp_spec.rb
26
39
  spec/parser_spec.rb
40
+ spec/pure_combine_spec.rb
41
+ spec/pure_def_spec.rb
42
+ spec/pure_define_method_spec.rb
43
+ spec/pure_eval_spec.rb
44
+ spec/pure_fun_spec.rb
45
+ spec/pure_nested_spec.rb
46
+ spec/pure_parser_spec.rb
47
+ spec/pure_spec.rb
48
+ spec/pure_spec_base.rb
49
+ spec/pure_splat_spec.rb
50
+ spec/pure_two_defs_spec.rb
51
+ spec/pure_worker_spec.rb
27
52
  spec/readme_spec.rb
28
53
  spec/splat_spec.rb
29
- spec/subseqent_spec.rb
30
- spec/timed_spec.rb
54
+ spec/worker_spec.rb
@@ -8,9 +8,8 @@ Language-level support for automatic parallelism and lazy evaluation.
8
8
  == Synopsis
9
9
 
10
10
  require 'pure'
11
- include Pure
12
11
 
13
- geometry = pure do
12
+ geometry = Pure.define do
14
13
  def area(width, height)
15
14
  width*height
16
15
  end
@@ -28,32 +27,570 @@ Language-level support for automatic parallelism and lazy evaluation.
28
27
  end
29
28
  end
30
29
 
31
- # compute the area using 3 parallel threads
32
- puts geometry.compute(:area, :threads => 3)
30
+ # Compute the area using 3 parallel threads.
31
+ puts geometry.compute(3).area
33
32
  # => 63
34
33
 
35
34
  # We've done this computation.
36
35
  puts((7 + 2)*(5 + 2))
37
36
  # => 63
38
37
 
39
- == Description
38
+ == Install
40
39
 
41
- +Pure+ is an importation of the pure functional paradigm into Ruby.
40
+ % gem install pure
42
41
 
43
- Method names and argument names have lexicographical meaning within a
44
- +pure+ block. Above, the +width+ argument to +area+ corresponds by
45
- its literal name to the +width+ method, for example.
42
+ Or for the (non-gem) .tgz package,
46
43
 
47
- +pure+ returns a module which may be included into other +pure+ blocks.
44
+ % ruby install.rb [--uninstall]
45
+
46
+ == Description
48
47
 
49
- Implementation details are placed inside Pure::PurePrivate, making
50
- <tt>include Pure</tt> a hygienic operation.
48
+ Pure imports aspects of the pure functional paradigm into Ruby.
49
+
50
+ Method and argument names have literal meaning within a
51
+ <tt>Pure.define</tt> block. In the above example, the +width+
52
+ argument to +area+ corresponds, by its literal name, to the +width+
53
+ method.
51
54
 
52
55
  Pure does not modify any of the standard classes.
53
56
 
54
- == Implementation
57
+ Pure has been tested on MRI 1.8.6, 1.8.7, 1.9.1, 1.9.2, and
58
+ jruby-1.4.
59
+
60
+ == Links
61
+
62
+ * Documentation: http://purefunctional.rubyforge.org
63
+ * Download: http://rubyforge.org/frs/?group_id=8324
64
+ * Rubyforge home: http://rubyforge.org/projects/purefunctional
65
+ * Repository: http://github.com/quix/pure
66
+
67
+ == Terminology
68
+
69
+ <tt>Pure.define</tt> returns a Module instance, called a <em>pure
70
+ module</em>. The methods of a pure module are called <em>pure
71
+ functions</em>.
72
+
73
+ == DSL
74
+
75
+ The pseudo-keyword +pure+, an alias of Pure.define, is included in the
76
+ global scope with <tt>require 'pure/dsl'</tt>. It is also made
77
+ available by including Pure::DSL into a class or module.
78
+
79
+ == Overrides
80
+
81
+ An options hash given to +compute+ is used for overriding functions in
82
+ the pure module. If the name of a function matches a hash key, the
83
+ function will be replaced with the corresponding hash value.
84
+
85
+ require 'pure/dsl'
86
+
87
+ greet = pure do
88
+ def hello(name)
89
+ "Hello, #{name}."
90
+ end
91
+
92
+ def name
93
+ "Bob"
94
+ end
95
+ end
96
+
97
+ puts greet.compute(9).hello
98
+ # => Hello, Bob.
99
+
100
+ puts greet.compute(9, :name => "Ralph").hello
101
+ # => Hello, Ralph.
102
+
103
+ puts greet.compute(9, :hello => "Good evening.").hello
104
+ # => Good evening.
105
+
106
+ == Default Number of Functions in Parallel
107
+
108
+ The _num_parallel_ argument to compute() may be omitted, in which case
109
+ the _num_parallel_ determination falls to <tt>Pure.worker</tt>, an
110
+ object described later in this document.
111
+
112
+ require 'pure/dsl'
113
+
114
+ Pure.worker.num_parallel = 2
115
+
116
+ result = pure do
117
+ def f(x, y)
118
+ x + y
119
+ end
120
+
121
+ def x
122
+ 33
123
+ end
124
+ end.compute(:y => 44)
125
+
126
+ # compute with 2 parallel threads
127
+ puts result.f # => 77
128
+
129
+ == Delegates and Blocks
130
+
131
+ When no block is given to +compute+, it returns a delegate for the
132
+ computation. The computation results are stored in the
133
+ lazily-evaluated attributes of the delegate. A computation is
134
+ performed only when an attribute is requested, and an attribute is
135
+ never recomputed.
136
+
137
+ When +compute+ is given a block, the delegate is passed to the block
138
+ and the return value of +compute+ is the result of the block.
139
+
140
+ require 'pure/dsl'
141
+
142
+ geometry = pure do
143
+ def area(width, height)
144
+ width*height
145
+ end
146
+
147
+ def width(border)
148
+ 7 + border
149
+ end
150
+
151
+ def height(border)
152
+ 5 + border
153
+ end
154
+ end
55
155
 
56
- The "ripper" parser is used if it exists, otherwise ruby_parser is
57
- used.
156
+ area = geometry.compute :border => 2 do |result|
157
+ puts result.border # => 2
158
+ puts result[:border] # => 2
159
+
160
+ puts result.width # => 9
161
+ puts result.height # => 7
162
+
163
+ result.area
164
+ end
165
+
166
+ puts area # => 63
167
+
168
+ Function results may also be accessed with <tt>[]</tt>, as shown in
169
+ <tt>result[:border]</tt> above.
170
+
171
+ == Combining Pure Modules
172
+
173
+ Pure modules are regular Module instances. They may be combined
174
+ freely with +include+.
175
+
176
+ require 'pure/dsl'
177
+
178
+ greet = pure do
179
+ def hello(name)
180
+ "Hello, #{name}."
181
+ end
182
+ end
183
+
184
+ ralph = pure do
185
+ include greet
186
+ def name
187
+ "Ralph"
188
+ end
189
+ end
190
+
191
+ puts ralph.compute.hello
192
+ # => Hello, Ralph.
193
+
194
+ == Dynamic Names
195
+
196
+ The pseudo-keyword +fun+ is provided for defining a pure function
197
+ whose name or arguments are unknown at compile time.
198
+
199
+ require 'pure/dsl'
200
+
201
+ geometry = pure do
202
+ fun :area => [:width, :height] do |w, h|
203
+ w*h
204
+ end
205
+
206
+ def width
207
+ 4
208
+ end
209
+
210
+ fun :height do
211
+ 5
212
+ end
213
+ end
214
+
215
+ puts geometry.compute.area # => 20
216
+
217
+ Or more realistically,
218
+
219
+ require 'pure/dsl'
220
+
221
+ stats = pure do
222
+ files = Dir["*"]
223
+
224
+ files.each { |file|
225
+ fun file do
226
+ File.stat(fun_name.to_s)
227
+ end
228
+ }
229
+
230
+ fun :total_size => files do |*values|
231
+ values.inject(0) { |acc, stat| acc + stat.size }
232
+ end
233
+ end
234
+
235
+ stats.compute { |result|
236
+ puts result["Rakefile"].size # => 505
237
+ puts result.total_size # => 39355
238
+ }
239
+
240
+ The left of side <tt>=></tt> is the function name. The right side is
241
+ an array containing the names of the function arguments. The values
242
+ of the function arguments are passed to the block.
243
+
244
+ The next section explains the +fun_name+ call in this example.
245
+
246
+ == Referencing Function and Argument Names
247
+
248
+ Inside a pure function, +fun_name+ gives the name of the function and
249
+ +arg_names+ gives the names of its arguments. In the previous example
250
+ above,
251
+
252
+ files.each { |file|
253
+ fun file do
254
+ File.stat(fun_name.to_s)
255
+ end
256
+ }
257
+
258
+ Here, <tt>fun_name.to_s</tt> is exactly the same as +file+. So why
259
+ not call <tt>File.stat(file)</tt>? Pure functions are extracted from
260
+ their surrounding context and must therefore use function arguments as
261
+ the sole means of communication. In this case
262
+ <tt>File.stat(file)</tt> references +file+ which lies outside the
263
+ function definition.
264
+
265
+ The above is strictly not necessary when the default worker (explained
266
+ later) is used, however the best strategy is to ignore this detail.
267
+
268
+ == Restrictions
269
+
270
+ Since the grand scheme of Pure rests upon all functions and function
271
+ arguments having a name, a pure function defined with +def+ cannot
272
+ have a <tt>*splat</tt> argument. Naturally this restriction does not
273
+ apply to pure functions defined with +fun+.
274
+
275
+ require 'pure/dsl'
276
+
277
+ pure do
278
+ def f(*args) # => raises Pure::SplatError
279
+ end
280
+
281
+ fun :g => [:x, :y] do |*args| # OK
282
+ args.map { |a| a**2 }
283
+ end
284
+ end
285
+
286
+ A block is never passed to a pure function (except if called manually,
287
+ of course).
288
+
289
+ A pure function cannot have default arguments.
290
+
291
+ A pure function should not reference variables declared outside the
292
+ function definition (see example in previous section).
293
+
294
+ == Background
295
+
296
+ The user should have a basic understanding of <em>functional
297
+ programming</em> (see for example
298
+ http://en.wikipedia.org/wiki/Functional_programming) and the meaning
299
+ of <em>side effects</em>.
300
+
301
+ Every pure function you define must explicitly depend on the data it uses.
302
+
303
+ #
304
+ # BAD example: depending on state DATA.value
305
+ #
306
+ geometry = pure do
307
+ def area(width, height)
308
+ width*height - DATA.value
309
+ end
310
+ end
311
+
312
+ Unless offset <tt>DATA.value</tt> is really a constant, the
313
+ computation result is in general not well-defined.
314
+
315
+ Just as depending on some changeable state is bad, it is likewise bad
316
+ to affect a state (to produce a <em>side effect</em>).
317
+
318
+ #
319
+ # BAD example: affecting state
320
+ #
321
+ geometry = pure do
322
+ def area(width, height)
323
+ ACCUMULATOR.add "more data"
324
+ width*height
325
+ end
326
+ end
327
+
328
+ Given a pure computation where functions are modifying +ACCUMULATOR+,
329
+ the end state of +ACCUMULATOR+ is not well-defined, even if
330
+ the methods of +ACCUMULATOR+ are thread-safe.
331
+
332
+ == Philosophy
333
+
334
+ Languages which are purely functional (e.g. Haskell) employ special
335
+ constructs (e.g. monads) for dealing with side-effects. This project
336
+ is roughly analogous to the converse with respect to Ruby.
337
+
338
+ Haskell code is pure (non-side-effecting) by default, with non-pure
339
+ operations being stuffed into monads. Ruby code is non-pure
340
+ (side-effecting) by default, with pure code being stuffed into +pure+
341
+ blocks.
342
+
343
+ == Purpose
344
+
345
+ Pure has two main goals:
346
+
347
+ * Parallelize system-intensive code, e.g. system() calls.
348
+
349
+ * Provide a framework for parallelizing Ruby code across an arbitrary number of cores/machines.
350
+
351
+ Due to the global VM lock in Ruby 1.9, the actual execution of Ruby VM
352
+ instructions is not parallelized. However when a Ruby thread is
353
+ blocking during a system call, other threads will be executed.
354
+ Contrariwise in Ruby 1.8 the whole interpreter is blocked during a
355
+ system call.
356
+
357
+ The next section addresses the second point above.
358
+
359
+ == Technical Details
360
+
361
+ === Parser Plugins
362
+
363
+ Pure uses a parser plugin to extract the +def+ and +fun+ definitions
364
+ inside a pure module. Three parsers are bundled with Pure,
365
+
366
+ * Pure::Parser::Internal -- <tt>require 'pure/parser/internal'</tt> -- ruby-1.9.2 only
367
+ * Pure::Parser::Ripper -- <tt>require 'pure/parser/ripper'</tt> -- ruby-1.9 only
368
+ * Pure::Parser::RubyParser -- <tt>require 'pure/parser/ruby_parser'</tt> -- any ruby
369
+
370
+ The default is tried in that order.
371
+
372
+ The current parser may be changed via the <tt>Pure.parser</tt>
373
+ attribute. A pure module is tied to a parser when the module is
374
+ created.
375
+
376
+ The only requirement for a parser plugin is to properly implement an
377
+ extract() method.
378
+
379
+ === Worker Plugins
380
+
381
+ A worker plugin is a class which defines what happens when a pure
382
+ function is triggered to execute. A worker instance is tied to the
383
+ computation delegate returned by compute().
384
+
385
+ The default worker looks like this:
386
+
387
+ module Pure
388
+ class NativeWorker
389
+ attr_reader :num_parallel
390
+
391
+ def define_function_begin(pure_module, num_parallel)
392
+ @num_parallel = num_parallel || self.class.num_parallel
393
+ @class = Class.new Names do
394
+ include pure_module
395
+ end
396
+ end
397
+
398
+ def define_function(spec)
399
+ lambda { |*args|
400
+ @class.new(spec[:name], spec[:args]).send(spec[:name], *args)
401
+ }
402
+ end
403
+
404
+ def define_function_end
405
+ end
406
+
407
+ class << self
408
+ attr_accessor :num_parallel
409
+ end
410
+ @num_parallel = 1
411
+ end
412
+ end
413
+
414
+ The following example illustrates the internals.
415
+
416
+ require 'pure/dsl'
417
+ require 'pure/parser/ruby_parser'
418
+
419
+ class FakeWorker
420
+ #
421
+ # This method is called for each pure function in the pure module.
422
+ #
423
+ # Returns a lambda which computes the function described by spec.
424
+ #
425
+ # For this fake worker, we just return the function info.
426
+ #
427
+ def define_function(spec)
428
+ lambda { |*args|
429
+ [spec, args]
430
+ }
431
+ end
432
+
433
+ #
434
+ # Called before all define_function calls.
435
+ #
436
+ # pure_module is the receiver of compute().
437
+ #
438
+ # num_parallel is the hint passed to compute(), or nil if no
439
+ # hint was given. A worker is free to ignore it, as we do here.
440
+ #
441
+ def define_function_begin(pure_module, num_parallel)
442
+ end
443
+
444
+ #
445
+ # Called after all define_function calls.
446
+ #
447
+ def define_function_end
448
+ end
449
+
450
+ #
451
+ # When a computation begins, the parallelizing engine asks the
452
+ # worker how many functions to run in parallel.
453
+ #
454
+ def num_parallel
455
+ 2
456
+ end
457
+
458
+ class << self
459
+ #
460
+ # A num_parallel hint for this worker. A worker is free to
461
+ # ignore this as well.
462
+ #
463
+ attr_accessor :num_parallel
464
+ end
465
+ end
466
+
467
+ adder = pure(Pure::Parser::RubyParser) do
468
+ def add(left, right)
469
+ left + right
470
+ end
471
+ end
472
+
473
+ require 'pp'
474
+ pp adder.compute(FakeWorker, :left => 33, :right => 44).add
475
+
476
+ #### output:
477
+
478
+ [{:name=>:add,
479
+ :args=>[:left, :right],
480
+ :code=>
481
+ s(:defn,
482
+ :add,
483
+ s(:args, :left, :right),
484
+ s(:scope,
485
+ s(:block, s(:call, s(:lvar, :left), :+, s(:arglist, s(:lvar, :right)))))),
486
+ :splat=>false,
487
+ :default=>false,
488
+ :file=>"fake_worker.rb",
489
+ :line=>29,
490
+ :origin=>:def,
491
+ :parser=>"Pure::Parser::RubyParser"},
492
+ [33, 44]]
493
+
494
+ Note the Pure::Parser::Internal parser will not generate a
495
+ <tt>:code</tt> entry, as it just calls Method#parameters and does no
496
+ parsing.
497
+
498
+ === Compilers
499
+
500
+ A compiler converts a function spec (the hash in the previous output)
501
+ into a callable Ruby object.
502
+
503
+ With the addition of a compiler, we have all the components necessary
504
+ for distributing computations. A function definition and its inputs
505
+ may be reconstructed on another Ruby interpreter.
506
+
507
+ require 'pure/dsl'
508
+ require 'pure/parser/ruby_parser'
509
+ require 'pure/compiler/ruby_parser'
510
+
511
+ class ExternalWorker
512
+ def initialize
513
+ @compiler = Pure::Compiler::RubyParser.new
514
+ end
515
+
516
+ def define_function(spec)
517
+ lambda { |*args|
518
+ @compiler.evaluate_function(spec, *args)
519
+ }
520
+ end
521
+
522
+ def define_function_begin(pure_module, num_parallel)
523
+ end
524
+
525
+ def define_function_end
526
+ end
527
+
528
+ def num_parallel
529
+ 2
530
+ end
531
+
532
+ class << self
533
+ attr_accessor :num_parallel
534
+ end
535
+ end
536
+
537
+ pure(Pure::Parser::RubyParser) do
538
+ def add(left, right)
539
+ left + right
540
+ end
541
+ end.compute(ExternalWorker, :left => 33, :right => 44) do |result|
542
+ puts result.add # => 77
543
+ end
544
+
545
+ See http://github.com/quix/tiamat for an example of a
546
+ multi-core/multi-machine worker plugin for Pure.
547
+
548
+ Pure::Compiler::RubyParser uses RubyParser and Ruby2Ruby together with
549
+ a code transformer (for instantiating +fun+ definitions) to compile a
550
+ function spec.
551
+
552
+ == Tools
553
+
554
+ Lobby your local ruby-core representative to add a built-in sexp
555
+ extractor and compiler. Lacking these tools, we are stuck this
556
+ inefficient roundabout process:
557
+
558
+ * Ruby interpreter parses the source
559
+ * RubyParser (redundantly) parses the source and converts it to an sexp
560
+ * Ruby2Ruby converts the sexp to a string containing Ruby code
561
+ * call +eval+ on the code string
562
+
563
+ However this could be reduced to (for example):
564
+
565
+ * Ruby interpreter parses the source
566
+ * call Proc#to_sexp
567
+ * call Proc.from_sexp
568
+
569
+ == Author
570
+
571
+ * James M. Lawrence <quixoticsycophant@gmail.com>
572
+
573
+ == License
574
+
575
+ Copyright (c) 2009 James M. Lawrence. All rights reserved.
576
+
577
+ Permission is hereby granted, free of charge, to any person
578
+ obtaining a copy of this software and associated documentation files
579
+ (the "Software"), to deal in the Software without restriction,
580
+ including without limitation the rights to use, copy, modify, merge,
581
+ publish, distribute, sublicense, and/or sell copies of the Software,
582
+ and to permit persons to whom the Software is furnished to do so,
583
+ subject to the following conditions:
584
+
585
+ The above copyright notice and this permission notice shall be
586
+ included in all copies or substantial portions of the Software.
587
+
588
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
589
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
590
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
591
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
592
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
593
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
594
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
595
+ SOFTWARE.
58
596
 
59
- Pure has been tested on MRI 1.8.6, 1.8.7, 1.9.1 and the latest jruby.