pure 0.1.0 → 0.2.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 (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.