dyoder-functor 0.3.1 → 0.4.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 (4) hide show
  1. data/doc/README +31 -5
  2. data/lib/functor.rb +12 -4
  3. data/test/guards.rb +18 -8
  4. metadata +32 -3
data/doc/README CHANGED
@@ -1,4 +1,8 @@
1
- Functor provides pattern-based function and method dispatch for Ruby. To use it in a class:
1
+ Functor provides pattern-based function and method dispatch for Ruby, originally inspired by Topher Cyll's multi gem.
2
+
3
+ = Method Functors
4
+
5
+ To use it in a class:
2
6
 
3
7
  class Repeater
4
8
  attr_accessor :times
@@ -13,16 +17,18 @@ Functor provides pattern-based function and method dispatch for Ruby. To use it
13
17
  r.repeat( "-" ) # => "- - - - -"
14
18
  r.repeat( 7.3 ) # => ArgumentError!
15
19
 
16
- Warning: This defines a class instance variable @__functors behind the scenes as a side-effect. Also, although inheritance works within a functor method, super does not. To call the parent method, you need to call it explicitly using the #functors class method, like this:
20
+ Warning: This defines a class instance variable <tt>@__functors</tt> behind the scenes as a side-effect. Also, although inheritance works within a functor method, super does not. To call the parent method, you need to call it explicitly using the <tt>#functors</tt> class method, like this:
17
21
 
18
22
  A.functors[ :foo ].apply( self, 'bar' )
19
23
 
24
+ = Stand-Alone Functors
25
+
20
26
  You can also define Functor objects directly:
21
27
 
22
28
  fib = Functor.new do
23
29
  given( 0 ) { 0 }
24
30
  given( 1 ) { 1 }
25
- given( 2..10000 ) { |n| self.call( n - 1 ) + self.call( n - 2 ) }
31
+ given( Integer ) { |n| self.call( n - 1 ) + self.call( n - 2 ) }
26
32
  end
27
33
 
28
34
  You can use functors directly with functions taking a block like this:
@@ -33,7 +39,9 @@ You can call a functor as a method using #apply:
33
39
 
34
40
  fun.apply( obj, 7 )
35
41
 
36
- which is actually how the method dispatch is implemented.
42
+ which is actually how the method functors are implemented.
43
+
44
+ = Pattern Matching
37
45
 
38
46
  Arguments are matched first using === and then ==, so anything that supports these methods can be matched against. In addition, you may pass "guards," any object that responds to #call and which take and object (the argument) and return true or false. This allows you to do things like this:
39
47
 
@@ -42,4 +50,22 @@ Arguments are matched first using === and then ==, so anything that supports the
42
50
  given( lambda { |x| x % 2 == 1 } ) { 'silver' }
43
51
  end
44
52
 
45
- which will return "white" and "silver" alternately for a sequence of numbers.
53
+ which will return "white" and "silver" alternately for a sequence of numbers.
54
+
55
+ = Precedence
56
+
57
+ Precedence is defined in order of declaration: first-come, first-serve, aka FIFO. Thus, you need to be careful in how you define your functor. The Fibonacci example above would not work properly if the Integer pattern was given first. That said, it is possible to redefine earlier cases, which, in effect, "demotes" it, as if it had not been declared before. So the following will work properly:
58
+
59
+ fib = Functor.new do
60
+ given( Integer ) { |n| raise "this would start an infinite loop ..." }
61
+ given( 0 ) { 0 }
62
+ given( 1 ) { 1 }
63
+ # but this will "demote" the Integer pattern and now it will work ...
64
+ given( Integer ) { |n| self.call( n - 1 ) + self.call( n - 2 ) }
65
+ end
66
+
67
+ This isn't perfect, but it is very easy to predict, simple to implement, and reasonably fast, which other approaches (such as implementing a precedence scheme) are not.
68
+
69
+ = Credits And Support
70
+
71
+ Functor was written by Dan Yoder, Matthew King, and Lawrence Pit. Send email to dan at zeraweb.com for support or questions.
data/lib/functor.rb CHANGED
@@ -11,7 +11,7 @@ class Functor
11
11
  klass = self.name ; module_eval <<-CODE
12
12
  def #{name}( *args, &block )
13
13
  begin
14
- #{klass}.functors[ :#{name} ].apply( self, *args, &block )
14
+ rval = #{klass}.functors[ :#{name} ].apply( self, *args, &block )
15
15
  rescue ArgumentError => e
16
16
  begin
17
17
  super
@@ -19,6 +19,11 @@ class Functor
19
19
  raise e
20
20
  end
21
21
  end
22
+ if rval.is_a? Continuation
23
+ super ; rval.call
24
+ else
25
+ rval
26
+ end
22
27
  end
23
28
  CODE
24
29
  end
@@ -52,13 +57,16 @@ class Functor
52
57
  def match( *args, &block )
53
58
  args.push( block ) if block_given?
54
59
  pattern, action = @rules.find { | pattern, action | match?( args, pattern ) }
55
- raise ArgumentError.new( "argument mismatch for argument(s): #{args.inspect}." ) unless action
60
+ raise ArgumentError.new( "argument mismatch for argument(s): #{ args.map { |arg| arg.inspect }.join(', ') }." ) unless action
56
61
  return action
57
62
  end
58
63
 
59
64
  def match?( args, pattern )
60
- pattern.zip(args).all? { |x,y| x === y or x == y or
61
- ( x.respond_to?(:call) && x.call( y ) ) } if pattern.length == args.length
65
+ args.zip( pattern ).all? { | arg, rule | pair?( arg, rule ) } if args.length == pattern.length
66
+ end
67
+
68
+ def pair?( arg, rule )
69
+ ( rule.is_a?( Proc ) and rule.call( arg ) ) or rule === arg or rule == arg
62
70
  end
63
71
 
64
72
  end
data/test/guards.rb CHANGED
@@ -1,15 +1,25 @@
1
1
  require 'test/helpers'
2
2
 
3
- stripe ||= Functor.new do
4
- given( lambda { |x| x % 2 == 0 } ) { 'white' }
5
- given( lambda { |x| x % 2 == 1 } ) { 'silver' }
6
- end
3
+ describe "Dispatch should support guards" do
4
+
5
+ before do
6
+ @stripe = Functor.new do
7
+ given( lambda { |x| x % 2 == 0 } ) { 'white' }
8
+ given( lambda { |x| x % 2 == 1 } ) { 'silver' }
9
+ end
7
10
 
8
- describe "Dipatch should support guards" do
11
+ @safe_divide = Functor.new do
12
+ given( lambda { |x| x == 0 }, Integer ) { |x,y| false }
13
+ given( Integer, Integer ) { |x,y| ( y / ( x * 1.0 )) }
14
+ end
15
+ end
9
16
 
10
- specify "allowing you to use odd or even numbers as a dispatcher" do
11
- [*0..9].map( &stripe ).should == %w( white silver ) * 5
17
+ specify "such as odd or even numbers" do
18
+ [*0..9].map( &@stripe ).should == %w( white silver ) * 5
12
19
  end
13
20
 
14
- end
21
+ specify "even with multiple arguments" do
22
+ @safe_divide.call( 0,7 ).should == false
23
+ end
15
24
 
25
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dyoder-functor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Yoder
@@ -11,7 +11,7 @@ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
13
 
14
- date: 2008-06-15 00:00:00 -07:00
14
+ date: 2008-06-29 00:00:00 -07:00
15
15
  default_executable:
16
16
  dependencies: []
17
17
 
@@ -25,6 +25,35 @@ extra_rdoc_files: []
25
25
 
26
26
  files:
27
27
  - doc/HISTORY
28
+ - doc/rdoc
29
+ - doc/rdoc/classes
30
+ - doc/rdoc/classes/Functor
31
+ - doc/rdoc/classes/Functor/Method.html
32
+ - doc/rdoc/classes/Functor/Method.src
33
+ - doc/rdoc/classes/Functor/Method.src/M000006.html
34
+ - doc/rdoc/classes/Functor.html
35
+ - doc/rdoc/classes/Functor.src
36
+ - doc/rdoc/classes/Functor.src/M000001.html
37
+ - doc/rdoc/classes/Functor.src/M000002.html
38
+ - doc/rdoc/classes/Functor.src/M000003.html
39
+ - doc/rdoc/classes/Functor.src/M000004.html
40
+ - doc/rdoc/classes/Functor.src/M000005.html
41
+ - doc/rdoc/classes/Object.html
42
+ - doc/rdoc/classes/Object.src
43
+ - doc/rdoc/classes/Object.src/M000007.html
44
+ - doc/rdoc/created.rid
45
+ - doc/rdoc/files
46
+ - doc/rdoc/files/doc
47
+ - doc/rdoc/files/doc/HISTORY.html
48
+ - doc/rdoc/files/doc/README.html
49
+ - doc/rdoc/files/lib
50
+ - doc/rdoc/files/lib/functor_rb.html
51
+ - doc/rdoc/files/lib/object_rb.html
52
+ - doc/rdoc/fr_class_index.html
53
+ - doc/rdoc/fr_file_index.html
54
+ - doc/rdoc/fr_method_index.html
55
+ - doc/rdoc/index.html
56
+ - doc/rdoc/rdoc-style.css
28
57
  - doc/README
29
58
  - lib/functor.rb
30
59
  - lib/object.rb
@@ -57,7 +86,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
57
86
  requirements: []
58
87
 
59
88
  rubyforge_project: functor
60
- rubygems_version: 1.0.1
89
+ rubygems_version: 1.2.0
61
90
  signing_key:
62
91
  specification_version: 2
63
92
  summary: Pattern-based dispatch for Ruby, inspired by Topher Cyll's multi.