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.
- data/doc/README +31 -5
- data/lib/functor.rb +12 -4
- data/test/guards.rb +18 -8
- metadata +32 -3
data/doc/README
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
-
Functor provides pattern-based function and method dispatch for Ruby
|
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
|
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(
|
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
|
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
|
-
|
61
|
-
|
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
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
-
|
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 "
|
11
|
-
[*0..9].map(
|
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
|
-
|
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.
|
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-
|
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
|
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.
|