multi 0.1

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/amulti.rb +40 -0
  2. data/multi.rb +139 -0
  3. data/smulti.rb +56 -0
  4. metadata +47 -0
@@ -0,0 +1,40 @@
1
+ # = amulti.rb - Array destructuring multiple dispatch for Ruby
2
+ #
3
+ # Copyright 2005, Christopher Cyll
4
+ # mailto: christopher at gmail dot com
5
+ #
6
+ # == Example
7
+ #
8
+ # === Array Dispatch (using 'amulti') ===
9
+ #
10
+
11
+ require 'multi'
12
+
13
+ def amulti(method_name, *patterns, &body)
14
+ Multi::DISPATCHER.add(Multi::ArrayDispatch, self, method_name, patterns, body)
15
+ end
16
+
17
+ module Multi
18
+ class ArrayDispatch < Dispatch
19
+ def initialize(patterns, body)
20
+ @count = patterns.size
21
+ super(patterns, body)
22
+ end
23
+
24
+ def match?(params)
25
+ return false if params.size != 1
26
+ # Call .to_a here?
27
+ array = params.first
28
+ return false if ! array.kind_of?(Array)
29
+ return false if array.size < @count
30
+ return super(array[0, @count])
31
+ end
32
+
33
+ def call(params, block)
34
+ array = params.first
35
+ use = array[0, @count]
36
+ use.push(array[@count..-1])
37
+ super(use, block)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,139 @@
1
+ # = multi.rb - Multiple Dispatch and Pattern Matching for Ruby
2
+ #
3
+ # Copyright 2005, Christopher Cyll
4
+ # mailto: christopher at gmail dot com
5
+ #
6
+ # == Example
7
+ #
8
+ # === Factorial Function
9
+ #
10
+ # require 'multi'
11
+ # multi(:fac, 0) { 1 }
12
+ # multi(:fac, Integer) {|x| x * fac(x-1)}
13
+ # fac(5) ==> 120
14
+ #
15
+ # === List Reversal Function
16
+ #
17
+ # require 'multi'
18
+ # multi(:reverse, []) { [] }
19
+ # multi(:reverse, Array) {|list| [list.pop] + reverse(list) }
20
+ # reverse([1,2,3]) ==> [3,2,1]
21
+ #
22
+ # === Method Dispatch
23
+ #
24
+ # require 'multi'
25
+ # class Foo
26
+ # multi(:hiya, 0) {|x| "Zero: #{x}" }
27
+ # multi(:hiya, Integer) {|x| "Int: #{x}" }
28
+ # multi(:hiya, String) {|x| "Str: #{x}" }
29
+ # end
30
+ #
31
+ # f = Foo.new()
32
+ # f.hiya(0) ==> "Zero: 0"
33
+ # f.hiya(5) ==> "Int: 5"
34
+ # f.hiya("hello") ==> "Str: hello"
35
+ #
36
+ # === Match Any ( _ in Haskell/ML) using Object
37
+ #
38
+ # require 'multi'
39
+ # multi(:baz, 3, Object) { 3 }
40
+ # multi(:baz, Object, String) {|o, str| str }
41
+ # baz(3, "three") ==> 3
42
+ # baz(2, "two") ==> "two"
43
+ #
44
+ # === Guards Using lambda/Proc
45
+ #
46
+ # multi(:gt2, lambda {|x| x > 2 }) {|x| x }
47
+ # multi(:gt2, Object) { 0 }
48
+ # gt2(1) ==> 0
49
+ # gt2(4) ==> 4
50
+ #
51
+ # === Returning Values From Multimethods
52
+ #
53
+ # multi(:evenify, Integer) do |x|
54
+ # next x if x % 2 == 0 # Return x using 'next'
55
+ # x += 1
56
+ # next x
57
+ # end
58
+ # evenify(4) ==> 4
59
+ # evenify(5) ==> 6
60
+ #
61
+ # === Declare a clause of a multimethod
62
+ #
63
+ # multi(:method_name, types_literals_or_guards) do |parameter1, parameter2|
64
+ # # code here!
65
+ # end
66
+
67
+ def multi(method_name, *patterns, &body)
68
+ Multi::DISPATCHER.add(Multi::Dispatch, self, method_name, patterns, body)
69
+ end
70
+
71
+ module Multi
72
+ class Dispatch
73
+ def initialize(patterns, body)
74
+ @patterns = patterns
75
+ @body = body
76
+ end
77
+
78
+ def match?(params)
79
+ pairs = params.zip(@patterns)
80
+ return pairs.all? do |param, pattern|
81
+ if pattern.kind_of?(Class)
82
+ param.kind_of?(pattern)
83
+ elsif pattern.instance_of?(Proc)
84
+ begin
85
+ pattern.call(param)
86
+ rescue
87
+ false
88
+ end
89
+ elsif pattern.instance_of?(Regexp)
90
+ pattern.match(param)
91
+ else
92
+ param == pattern
93
+ end
94
+ end
95
+ end
96
+
97
+ def call(params, block)
98
+ @body.call(*params, &block)
99
+ end
100
+ end
101
+
102
+ class Dispatcher
103
+ def initialize
104
+ @map = {}
105
+ end
106
+
107
+ def add(type, obj, method_name, patterns, body)
108
+ method_name = method_name.id2name if method_name.kind_of?(Symbol)
109
+ body = patterns.pop if body.nil?
110
+
111
+ # Using the object_id() is pretty sleazy, but it gives us faster
112
+ # lookup than object.equal? and searching
113
+ key = [obj.object_id(), method_name]
114
+ @map[key] ||= []
115
+ @map[key].push(type.new(patterns, body))
116
+
117
+ # Tried to use send(:define, ...) but Procs can't have &blocks
118
+ if ! obj.methods.include?(method_name)
119
+ obj.instance_eval <<-"DONE"
120
+ def #{method_name}(*params, &block)
121
+ Multi::DISPATCHER.call(self, \"#{method_name}\", params, block)
122
+ end
123
+ DONE
124
+ end
125
+ end
126
+
127
+ def call(obj, method_name, params, block)
128
+ dispatches = @map[[obj.object_id, method_name]]
129
+ dispatch = dispatches.find{|dispatch| dispatch.match?(params) }
130
+ if dispatch.nil?
131
+ printed_params = params.map{|param| param.inspect}.join(', ')
132
+ raise "No match for #{obj}.#{method_name}(#{printed_params})"
133
+ end
134
+ dispatch.call(params, block)
135
+ end
136
+ end
137
+
138
+ DISPATCHER = Dispatcher.new()
139
+ end
@@ -0,0 +1,56 @@
1
+ # = smulti.rb - String destructuring multiple dispatch for Ruby
2
+ #
3
+ # Copyright 2005, Christopher Cyll
4
+ # mailto: christopher at gmail dot com
5
+ #
6
+ # == Example
7
+ #
8
+ # === String Dispatch (using 'smulti') ===
9
+ #
10
+ # smulti(:foo, 'a') { puts "a FOUND" }
11
+ # smulti(:foo, /./) {|s, rest| foo(rest) }
12
+ # smulti(:foo, // ) { puts "a NOT FOUND" }
13
+ # foo('a') ==> "a FOUND"
14
+ # foo('') ==> "a NOT FOUND"
15
+ # foo('ab') ==> "a FOUND"
16
+ # foo('ba') ==> "a FOUND"
17
+ # foo('bb') ==> "a NOT FOUND"
18
+
19
+ require 'multi'
20
+
21
+ def smulti(method_name, *patterns, &body)
22
+ Multi::DISPATCHER.add(Multi::StringDispatch, self, method_name, patterns, body)
23
+ end
24
+
25
+ module Multi
26
+ class StringDispatch
27
+ def initialize(patterns, body)
28
+ @body = body
29
+
30
+ pattern = patterns.first
31
+ if pattern.kind_of?(String)
32
+ source = "\\A" + Regexp.escape(pattern)
33
+ @re = Regexp.new(source)
34
+ elsif pattern.kind_of?(Regexp)
35
+ opts = pattern.options
36
+ source = "\\A" + pattern.source
37
+ @re = Regexp.new(source, opts)
38
+ else
39
+ throw "Bad pattern #{pattern} for StringDispatch. Use a string or a regexp"
40
+ end
41
+ end
42
+
43
+ def match?(params)
44
+ return false if params.size != 1
45
+ @re.match(params.first)
46
+ end
47
+
48
+ def call(params, block)
49
+ match = @re.match(params.first)
50
+ results = match.captures
51
+ results.push(match[0]) if results.empty?
52
+ results.push(match.post_match)
53
+ @body.call(*results, &block)
54
+ end
55
+ end
56
+ end
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.11
3
+ specification_version: 1
4
+ name: multi
5
+ version: !ruby/object:Gem::Version
6
+ version: "0.1"
7
+ date: 2006-01-03 00:00:00 -08:00
8
+ summary: Multiple Dispatch/Pattern Matching for Ruby
9
+ require_paths:
10
+ - .
11
+ email: christophercyll@gmail.com
12
+ homepage: http://cyll.org/multi
13
+ rubyforge_project: multi
14
+ description:
15
+ autorequire: multi
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: false
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ authors:
29
+ - Topher Cyll
30
+ files:
31
+ - multi.rb
32
+ - smulti.rb
33
+ - amulti.rb
34
+ test_files: []
35
+
36
+ rdoc_options: []
37
+
38
+ extra_rdoc_files: []
39
+
40
+ executables: []
41
+
42
+ extensions: []
43
+
44
+ requirements: []
45
+
46
+ dependencies: []
47
+