multi_dispatch 0.1dev
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +23 -0
- data/Gemfile +0 -0
- data/LICENSE +22 -0
- data/README.md +124 -0
- data/Rakefile +7 -0
- data/TODO.md +7 -0
- data/examples/functional_style.rb +30 -0
- data/examples/lambda_calc.rb +162 -0
- data/examples/lc_interpreter.rb +154 -0
- data/examples/pattern_matching.rb +122 -0
- data/examples/pe_31.rb +23 -0
- data/lib/multi_dispatch/dispatch.rb +58 -0
- data/lib/multi_dispatch/multi_method.rb +34 -0
- data/lib/multi_dispatch/unbound_multi_method.rb +62 -0
- data/lib/multi_dispatch/version.rb +3 -0
- data/lib/multi_dispatch.rb +13 -0
- data/multi_dispatch.gemspec +24 -0
- data/tests/ambiguity_tests.rb +41 -0
- data/tests/multi_dispatch_tests.rb +87 -0
- metadata +93 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MDM5YjUyM2QxODYxMTY1YWJiM2VlMTQ1M2UwYTVjNjIxYWQ3ODY3MQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
OTU1OTY5YmQ5MmZiMzNmNmQ5M2M5MjMyOGUwOGY3ZTQ3ZGRjODhlMA==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
Zjk5NTlhMTRlYzRlYTI0YzIxYTQxZjIxY2FlNjU0MTJiY2MwNmEzMmJiMTlh
|
10
|
+
Y2RmMTE5YjAzMGI3ODNiM2NkOTVkYjNkYWQ3MmYwOWJkZWYwNDlhYWM0Y2Vl
|
11
|
+
MGQzMDhiN2YzZmUxZjU5MTdlNDAyZTAxZjhiNDg3ZGU0YTZmOWY=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MzUzNWM2NjBhOGE0ZGM5YzgzNDQ2ZTJhZDE3NDE1MmQ1MjY2ZDEyZjEwYTZj
|
14
|
+
ZTFhNzY2MjIzMDE4ZTczNGMzOGI3NWJhYzFkNGVjZjFmZmMyNGI0OWU3MmQ4
|
15
|
+
MjY2MmQwNTc4YTJhNDk3MjMyZGQxYWVmNDk5M2EyNTAxM2ZhMjI=
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
*.gem
|
2
|
+
*.kpf
|
3
|
+
*.rbc
|
4
|
+
*.sw?
|
5
|
+
*.un~
|
6
|
+
.DS_Store
|
7
|
+
.buildpath
|
8
|
+
.bundle
|
9
|
+
.config
|
10
|
+
.project
|
11
|
+
.yardoc
|
12
|
+
Gemfile.lock
|
13
|
+
InstalledFiles
|
14
|
+
_yardoc
|
15
|
+
coverage
|
16
|
+
doc/
|
17
|
+
lib/bundler/man
|
18
|
+
pkg
|
19
|
+
rdoc
|
20
|
+
spec/reports
|
21
|
+
test/tmp
|
22
|
+
test/version_tmp
|
23
|
+
tmp
|
data/Gemfile
ADDED
File without changes
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Robert Pozoga
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
MultiDispatch
|
2
|
+
====================
|
3
|
+
|
4
|
+
multiple-dispatch is a library extending Ruby objects with multiple dispatch generic functions.
|
5
|
+
|
6
|
+
Look for examples in the examples/ directory and in test cases test/. Here are simple examples:
|
7
|
+
|
8
|
+
Functional style
|
9
|
+
----------------
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
require_relative '../lib/multi_dispatch.rb'
|
13
|
+
|
14
|
+
# reverse for Array
|
15
|
+
MultiDispatch::def_multi :reverse, [] { [] }
|
16
|
+
MultiDispatch::def_multi :reverse, Array do |list|
|
17
|
+
[list.pop] + reverse(list)
|
18
|
+
end
|
19
|
+
|
20
|
+
# map for Array
|
21
|
+
MultiDispatch::def_multi :map, [], Proc do ; [] end
|
22
|
+
MultiDispatch::def_multi :map, Array, Proc do |list, func|
|
23
|
+
[func.call(list.first)] + map(list[1..-1], func)
|
24
|
+
end
|
25
|
+
|
26
|
+
# foldl for Array (equivalent to haskell implementation in Data.List module)
|
27
|
+
MultiDispatch::def_multi :foldl, Proc, Object, [] do |f, init, l|
|
28
|
+
init
|
29
|
+
end
|
30
|
+
MultiDispatch::def_multi :foldl, Proc, Object, Array do |f, init, l|
|
31
|
+
foldl(f, f.call(init, l.first), l.drop(1))
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
|
36
|
+
Pattern Matching
|
37
|
+
----------------
|
38
|
+
|
39
|
+
It allows to do hacky pattern matching using annonymous functions for defining parameters. The example below shows equivalent Ruby code for Racket examples of list pattern matching:
|
40
|
+
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
# (match '(1 2 3)
|
44
|
+
# [(list a b c) (list c b a)]) => '(3 2 1)
|
45
|
+
#
|
46
|
+
def_multi :ex1, lambda { |l| l.size == 3 } do |list|
|
47
|
+
a,b,c = list
|
48
|
+
[c, b, a]
|
49
|
+
end
|
50
|
+
|
51
|
+
# (match '(1 2 3)
|
52
|
+
# [(list 1 a ...) a]) => '(2 3)
|
53
|
+
#
|
54
|
+
def_multi :ex2, lambda { |l| l.first == 1 } do |list|
|
55
|
+
list[1..-1]
|
56
|
+
end
|
57
|
+
|
58
|
+
# (match '(1 (2) (2) (2) 5)
|
59
|
+
# [(list 1 (list a) ..3 5) a]
|
60
|
+
# [_ 'else]) => '(2 2 2)
|
61
|
+
#
|
62
|
+
ptr = lambda do |list|
|
63
|
+
list.first == 1 && list.size == 5 && list.last == 5 &&
|
64
|
+
list[1..-2].all? { |e| e.size == 1}
|
65
|
+
end
|
66
|
+
def_multi :ex5, ptr do |list|
|
67
|
+
list[1..3].map(&:first)
|
68
|
+
end
|
69
|
+
|
70
|
+
# (match '(1 2 3)
|
71
|
+
# [(list-no-order 3 2 x) x]) => 1
|
72
|
+
#
|
73
|
+
ptr = lambda do |list|
|
74
|
+
list.size == 3 && list.include?(2) && list.include?(3)
|
75
|
+
end
|
76
|
+
def_multi :ex6, ptr do |list|
|
77
|
+
list.delete_if { |x| [2,3].include? x }.first
|
78
|
+
end
|
79
|
+
|
80
|
+
# (match '(1 (2 3) 4)
|
81
|
+
# [(list _ (and a (list _ ...)) _) a]) => '(2 3)
|
82
|
+
#
|
83
|
+
ptr = lambda do |list|
|
84
|
+
list.size == 3 && list[1].size >= 1
|
85
|
+
end
|
86
|
+
def_multi :ex7, ptr do |list|
|
87
|
+
list[1]
|
88
|
+
end
|
89
|
+
```
|
90
|
+
|
91
|
+
Method Ambiguities
|
92
|
+
------------------
|
93
|
+
|
94
|
+
It is possible to define a set of methods such that there is no unique most specific method applicable to some combinations of arguments:
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
def_multi :arg, 1, Numeric do |*a|
|
98
|
+
a.first
|
99
|
+
end
|
100
|
+
def_multi :arg, Numeric, 2 do |*a|
|
101
|
+
a.last
|
102
|
+
end
|
103
|
+
|
104
|
+
# is_one?(1) matches to the method with argument defined by value
|
105
|
+
def_multi :is_one?, 1 do
|
106
|
+
true
|
107
|
+
end
|
108
|
+
def_multi :is_one?, lambda { |x| x == 1 } do
|
109
|
+
true
|
110
|
+
end
|
111
|
+
|
112
|
+
1.9.3-p374 :001 > arg(1,2)
|
113
|
+
2
|
114
|
+
|
115
|
+
1.9.3-p374 :001 > arg(1, 1337)
|
116
|
+
1
|
117
|
+
|
118
|
+
1.9.3-p374 :001 > arg(1337, 2)
|
119
|
+
2
|
120
|
+
|
121
|
+
```
|
122
|
+
|
123
|
+
|
124
|
+
|
data/Rakefile
ADDED
data/TODO.md
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative '../lib/multi_dispatch.rb'
|
2
|
+
|
3
|
+
|
4
|
+
# reverse for Array
|
5
|
+
MultiDispatch::def_multi :reverse, [] { [] }
|
6
|
+
MultiDispatch::def_multi :reverse, Array do |list|
|
7
|
+
[list.pop] + reverse(list)
|
8
|
+
end
|
9
|
+
|
10
|
+
p reverse [1,2,3,4,5]
|
11
|
+
|
12
|
+
# map for Array
|
13
|
+
MultiDispatch::def_multi :map, [], Proc do ; [] end
|
14
|
+
MultiDispatch::def_multi :map, Array, Proc do |list, func|
|
15
|
+
[func.call(list.first)] + map(list[1..-1], func)
|
16
|
+
end
|
17
|
+
|
18
|
+
p map [1,2,3], lambda { |x| x**2 }
|
19
|
+
|
20
|
+
# foldl for Array (equivalent to haskell Data.List implementation)
|
21
|
+
MultiDispatch::def_multi :foldl, Proc, Object, [] do |f, init, l|
|
22
|
+
init
|
23
|
+
end
|
24
|
+
MultiDispatch::def_multi :foldl, Proc, Object, Array do |f, init, l|
|
25
|
+
foldl(f, f.call(init, l.first), l.drop(1))
|
26
|
+
end
|
27
|
+
|
28
|
+
p foldl(lambda { |x, y| x+y }, 0, [1,2,3,4,5])
|
29
|
+
|
30
|
+
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require_relative '../lib/multi_dispatch.rb'
|
2
|
+
|
3
|
+
class Interpreter
|
4
|
+
include MultiDispatch
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@env = Env.new
|
8
|
+
end
|
9
|
+
|
10
|
+
class Env < Hash
|
11
|
+
end
|
12
|
+
|
13
|
+
class Expression
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
class Var < Expression
|
18
|
+
attr_accessor :name
|
19
|
+
def initialize(name)
|
20
|
+
@name = name
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Num < Expression
|
25
|
+
attr_accessor :value
|
26
|
+
def initialize(value) ; @value = value end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Bool < Expression
|
30
|
+
attr_accessor :value
|
31
|
+
def initialize(value) ; @value = value end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Plus < Expression
|
35
|
+
attr_accessor :left, :right
|
36
|
+
def initialize(left, right)
|
37
|
+
@left, @right = left, right
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Minus < Expression
|
42
|
+
attr_accessor :left, :right
|
43
|
+
def initialize(left, right)
|
44
|
+
@left, @right = left, right
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Func < Expression
|
49
|
+
attr_accessor :var_name, :body
|
50
|
+
def initialize(var_name, body)
|
51
|
+
@var_name, @body = var_name, body
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Closure
|
56
|
+
attr_accessor :var_name, :body
|
57
|
+
def initialize(var_name, body)
|
58
|
+
@var_name, @body = var_name, body
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class Call < Expression
|
63
|
+
attr_accessor :func, :param
|
64
|
+
def initialize(func, param)
|
65
|
+
@func, @param = func, param
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class LetDirect
|
70
|
+
attr_accessor :var, :expr, :body
|
71
|
+
def initialize(var, expr, body)
|
72
|
+
@var, @expr, @body = var, expr, body
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class LetByFunc
|
77
|
+
attr_accessor :var, :expr, :body
|
78
|
+
def initialize(var, expr, body)
|
79
|
+
@var, @expr, @body = var, expr, body
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class If
|
84
|
+
attr_accessor :cond, :if_true, :if_false
|
85
|
+
def initialize(cond, if_true, if_false)
|
86
|
+
@cond, @if_true, @if_false = cond, if_true, if_false
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def_multi :evaluate, Num do |num|
|
91
|
+
num.value
|
92
|
+
end
|
93
|
+
|
94
|
+
def_multi :evaluate, Plus do |plus|
|
95
|
+
evaluate(plus.left) + evaluate(plus.right)
|
96
|
+
end
|
97
|
+
|
98
|
+
def_multi :evaluate, Minus do |minus|
|
99
|
+
evaluate(minus.left) - evaluate(minus.right)
|
100
|
+
end
|
101
|
+
|
102
|
+
def_multi :evaluate, Bool do |bool|
|
103
|
+
bool.value
|
104
|
+
end
|
105
|
+
|
106
|
+
def_multi :evaluate, Var do |var|
|
107
|
+
@env[var.name]
|
108
|
+
end
|
109
|
+
|
110
|
+
def_multi :evaluate, Func do |func|
|
111
|
+
Closure.new(func.var_name, func.body)
|
112
|
+
end
|
113
|
+
|
114
|
+
def_multi :evaluate, Call do |call|
|
115
|
+
closure = evaluate(call.func)
|
116
|
+
@env[closure.var_name] = evaluate(call.param)
|
117
|
+
evaluate(closure.body)
|
118
|
+
end
|
119
|
+
|
120
|
+
def_multi :evaluate, If do |stat|
|
121
|
+
evaluate(stat.cond) ? evaluate(stat.if_true) : evaluate(stat.if_false)
|
122
|
+
end
|
123
|
+
|
124
|
+
def_multi :evaluate, LetDirect do |let_dir|
|
125
|
+
@env[let_dir.var] = evaluate(let_dir.expr)
|
126
|
+
ret = evaluate(let_dir.body) ; @env.delete(let_dir.var) ; ret
|
127
|
+
end
|
128
|
+
|
129
|
+
def_multi :evaluate, LetByFunc do |let_func|
|
130
|
+
evaluate(Call.new(Func.new(let_func.var, let_func.body), let_func.expr))
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
def test
|
135
|
+
p evaluate(Num.new(7))
|
136
|
+
p evaluate(Plus.new(Num.new(3), Num.new(5)))
|
137
|
+
p evaluate(Minus.new(Num.new(4), Num.new(4)))
|
138
|
+
p evaluate(Plus.new(Num.new(5), Minus.new(Num.new(2), Num.new(1))))
|
139
|
+
@env[:a] = 5
|
140
|
+
p evaluate(Var.new(:a))
|
141
|
+
p evaluate(Plus.new(Var.new(:a), Minus.new(Num.new(2), Num.new(1))))
|
142
|
+
p evaluate(Call.new(Func.new(:x, Plus.new(Var.new(:x), Num.new(1))), Num.new(2)))
|
143
|
+
p evaluate(Call.new(
|
144
|
+
Func.new(:f, Plus.new(Num.new(2), Call.new(Var.new(:f), Num.new(8)))),
|
145
|
+
Func.new(:x, Plus.new(Num.new(5), Var.new(:x)))))
|
146
|
+
p evaluate(Call.new(
|
147
|
+
Func.new(:f, Plus.new(Num.new(2), Call.new(Var.new(:f), Num.new(8)))),
|
148
|
+
Func.new(:f, Plus.new(Num.new(5), Var.new(:f)))))
|
149
|
+
|
150
|
+
p evaluate(LetDirect.new(:x, Num.new(28), Var.new(:x))) # 28
|
151
|
+
p evaluate(LetByFunc.new(:x, Num.new(18), Var.new(:x))) # 18
|
152
|
+
p evaluate(LetByFunc.new(:b, Bool.new(true), Var.new(:b))) # 6
|
153
|
+
p evaluate(LetByFunc.new(:b, Bool.new(true), If.new(Var.new(:b), Num.new(6), Num.new(7)))) # 6
|
154
|
+
p evaluate(LetByFunc.new(:b, Bool.new(false), If.new(Var.new("b"), Num.new(6), Num.new(7)))) # 7
|
155
|
+
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
inter = Interpreter.new
|
162
|
+
inter.test
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# Simple lambda calculus interpreter to evaluate arithmetic expressions
|
2
|
+
# in the form of abstract syntax trees. Has support for anonymous
|
3
|
+
# functions, closures, and let expressions.
|
4
|
+
#
|
5
|
+
# Implemented in functional way, using multi_dispatch gem.
|
6
|
+
|
7
|
+
require_relative '../lib/multi_dispatch.rb'
|
8
|
+
|
9
|
+
# helpers
|
10
|
+
module Camelizer
|
11
|
+
def camelize
|
12
|
+
return self.to_s if self !~ /_/ && self =~ /[A-Z]+.*/
|
13
|
+
to_s.split('_').map { |e| e.capitalize }.join
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Symbol.send(:include, Camelizer)
|
18
|
+
|
19
|
+
# Lambda Calculus Interpreter
|
20
|
+
class LCI
|
21
|
+
include MultiDispatch
|
22
|
+
|
23
|
+
attr_accessor :env
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
@env = {}
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.def_expr(name, *args)
|
30
|
+
klass = Object.const_set(name.camelize, Class.new)
|
31
|
+
klass.class_eval do
|
32
|
+
attr_accessor *args
|
33
|
+
define_method :initialize do |*values|
|
34
|
+
args.zip(values).each do |var, value|
|
35
|
+
instance_variable_set("@#{var}", value)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def_expr(:var, :name)
|
42
|
+
def_expr(:plus, :left, :right)
|
43
|
+
def_expr(:minus, :left, :right)
|
44
|
+
def_expr(:func, :var_name, :body)
|
45
|
+
def_expr(:closure, :var_name, :body)
|
46
|
+
def_expr(:call, :func, :param)
|
47
|
+
def_expr(:let_direct, :var, :expr, :body)
|
48
|
+
def_expr(:let_by_func, :var, :expr, :body)
|
49
|
+
def_expr(:if, :cond, :if_true, :if_false)
|
50
|
+
|
51
|
+
def_multi :evaluate, Numeric do |num| ; num end
|
52
|
+
def_multi :evaluate, true do ; true end
|
53
|
+
def_multi :evaluate, false do ; false end
|
54
|
+
|
55
|
+
def_multi :evaluate, Plus do |plus|
|
56
|
+
evaluate(plus.left) + evaluate(plus.right)
|
57
|
+
end
|
58
|
+
|
59
|
+
def_multi :evaluate, Minus do |minus|
|
60
|
+
evaluate(minus.left) - evaluate(minus.right)
|
61
|
+
end
|
62
|
+
|
63
|
+
def_multi :evaluate, Var do |var|
|
64
|
+
@env[var.name]
|
65
|
+
end
|
66
|
+
|
67
|
+
def_multi :evaluate, Func do |func|
|
68
|
+
Closure.new(func.var_name, func.body)
|
69
|
+
end
|
70
|
+
|
71
|
+
def_multi :evaluate, Call do |call|
|
72
|
+
closure = evaluate(call.func)
|
73
|
+
@env[closure.var_name] = evaluate(call.param)
|
74
|
+
evaluate(closure.body)
|
75
|
+
end
|
76
|
+
|
77
|
+
def_multi :evaluate, If do |stat|
|
78
|
+
evaluate(stat.cond) ? evaluate(stat.if_true) : evaluate(stat.if_false)
|
79
|
+
end
|
80
|
+
|
81
|
+
def_multi :evaluate, LetDirect do |let_dir|
|
82
|
+
@env[let_dir.var] = evaluate(let_dir.expr)
|
83
|
+
ret = evaluate(let_dir.body) ; @env.delete(let_dir.var) ; ret
|
84
|
+
end
|
85
|
+
|
86
|
+
def_multi :evaluate, LetByFunc do |let_func|
|
87
|
+
evaluate(Call.new(Func.new(let_func.var, let_func.body), let_func.expr))
|
88
|
+
end
|
89
|
+
|
90
|
+
class << self ; undef :def_expr end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
# *** TESTS ***
|
95
|
+
require 'minitest/autorun'
|
96
|
+
|
97
|
+
class TestStuff < MiniTest::Unit::TestCase
|
98
|
+
|
99
|
+
# helper for tests
|
100
|
+
def eval(expr)
|
101
|
+
LCI.new.evaluate(expr)
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_evaluate_to_7
|
105
|
+
assert_equal( 7,
|
106
|
+
eval(7))
|
107
|
+
end
|
108
|
+
def test_simple_addition
|
109
|
+
assert_equal( 8,
|
110
|
+
eval( Plus.new(3, 5)))
|
111
|
+
end
|
112
|
+
def test_nested_addition
|
113
|
+
assert_equal( 0,
|
114
|
+
eval( Minus.new(4, 4)))
|
115
|
+
end
|
116
|
+
def test_addition_and_subtraction
|
117
|
+
assert_equal( 6,
|
118
|
+
eval( Plus.new(5, Minus.new(2, 1))))
|
119
|
+
end
|
120
|
+
def test_eval_with_env_variable
|
121
|
+
i = LCI.new
|
122
|
+
i.env[:a] = 5
|
123
|
+
assert_equal( 5,
|
124
|
+
i.evaluate( Var.new(:a) ))
|
125
|
+
assert_equal( 6,
|
126
|
+
i.evaluate(Plus.new(Var.new(:a), Minus.new(2, 1))))
|
127
|
+
end
|
128
|
+
def test_functions
|
129
|
+
assert_equal( 3,
|
130
|
+
eval(Call.new(Func.new(:x, Plus.new(Var.new(:x), 1)), 2)))
|
131
|
+
end
|
132
|
+
def test_passing_functions_to_functions
|
133
|
+
assert_equal( 15,
|
134
|
+
eval(Call.new(
|
135
|
+
Func.new(:f, Plus.new( 2, Call.new( Var.new(:f), 8))),
|
136
|
+
Func.new( :x, Plus.new(5, Var.new(:x))))))
|
137
|
+
assert_equal( 15,
|
138
|
+
eval(Call.new(
|
139
|
+
Func.new(:f, Plus.new(2, Call.new(Var.new(:f), 8))),
|
140
|
+
Func.new(:f, Plus.new(5, Var.new(:f))))))
|
141
|
+
end
|
142
|
+
def test_let_direct
|
143
|
+
assert_equal( 28, eval(LetDirect.new(:x, 28, Var.new(:x))))
|
144
|
+
assert_equal( 18, eval(LetByFunc.new(:x, 18, Var.new(:x))))
|
145
|
+
end
|
146
|
+
def test_let_by_func
|
147
|
+
assert_equal( true, eval(LetByFunc.new(:b, true, Var.new(:b))))
|
148
|
+
assert_equal( 6, eval(LetByFunc.new(:b, true, If.new(Var.new(:b), 6, 7))))
|
149
|
+
assert_equal( 7, eval(LetByFunc.new(:b, false, If.new(Var.new(:b), 6, 7))))
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require_relative '../lib/multi_dispatch.rb'
|
2
|
+
|
3
|
+
class ListPatternMatching
|
4
|
+
include MultiDispatch
|
5
|
+
|
6
|
+
# (match '(1 2 3)
|
7
|
+
# [(list a b c) (list c b a)]) => '(3 2 1)
|
8
|
+
#
|
9
|
+
def_multi :ex1, lambda { |l| l.size == 3 } do |list|
|
10
|
+
a,b,c = list
|
11
|
+
[c, b, a]
|
12
|
+
end
|
13
|
+
|
14
|
+
# (match '(1 2 3)
|
15
|
+
# [(list 1 a ...) a]) => '(2 3)
|
16
|
+
#
|
17
|
+
def_multi :ex2, lambda { |l| l.first == 1 } do |list|
|
18
|
+
list[1..-1]
|
19
|
+
end
|
20
|
+
|
21
|
+
# (match '(1 2 3 4)
|
22
|
+
# [(list 1 a ..3) a]
|
23
|
+
# [_ 'else]) => '(2 3 4)
|
24
|
+
#
|
25
|
+
def_multi :ex3,
|
26
|
+
lambda { |l| l.first == 1 && l[1..-1].size >= 3 } do |list|
|
27
|
+
list[1..3]
|
28
|
+
end
|
29
|
+
|
30
|
+
# (match '(1 2 3 4 5)
|
31
|
+
# [(list 1 a ..3 5) a]
|
32
|
+
# [_ 'else]) => '(2 3 4)
|
33
|
+
#
|
34
|
+
def_multi :ex4,
|
35
|
+
lambda { |l| l.first == 1 && l.size == 5 && l.last == 5 } do |list|
|
36
|
+
list[1..3]
|
37
|
+
end
|
38
|
+
|
39
|
+
# (match '(1 (2) (2) (2) 5)
|
40
|
+
# [(list 1 (list a) ..3 5) a]
|
41
|
+
# [_ 'else]) => '(2 2 2)
|
42
|
+
#
|
43
|
+
ptr = lambda do |list|
|
44
|
+
list.first == 1 && list.size == 5 && list.last == 5 &&
|
45
|
+
list[1..-2].all? { |e| e.size == 1}
|
46
|
+
end
|
47
|
+
def_multi :ex5, ptr do |list|
|
48
|
+
list[1..3].map(&:first)
|
49
|
+
end
|
50
|
+
|
51
|
+
# (match '(1 2 3)
|
52
|
+
# [(list-no-order 3 2 x) x]) => 1
|
53
|
+
#
|
54
|
+
ptr = lambda do |list|
|
55
|
+
list.size == 3 && list.include?(2) && list.include?(3)
|
56
|
+
end
|
57
|
+
def_multi :ex6, ptr do |list|
|
58
|
+
list.delete_if { |x| [2,3].include? x }.first
|
59
|
+
end
|
60
|
+
|
61
|
+
# (match '(1 (2 3) 4)
|
62
|
+
# [(list _ (and a (list _ ...)) _) a]) => '(2 3)
|
63
|
+
#
|
64
|
+
ptr = lambda do |list|
|
65
|
+
list.size == 3 && list[1].size >= 1
|
66
|
+
end
|
67
|
+
def_multi :ex7, ptr do |list|
|
68
|
+
list[1]
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
class RegexPatternMatching
|
74
|
+
include MultiDispatch
|
75
|
+
|
76
|
+
animals_ptr = /dog|cat|cow|ox/
|
77
|
+
def_multi :classify, lambda { |s| s =~ animals_ptr } do |s|
|
78
|
+
s =~ animals_ptr ; [$&, :animal]
|
79
|
+
end
|
80
|
+
|
81
|
+
def_multi :classify, lambda { |s| s =~ /-?\d+/ } do |s|
|
82
|
+
s =~ /-?\d*/ ; [$&, :integer]
|
83
|
+
end
|
84
|
+
|
85
|
+
food_ptr = /burger|pizza|falafel|burrito|pasta/
|
86
|
+
def_multi :classify, lambda { |s| s =~ food_ptr } do |s|
|
87
|
+
s =~ food_ptr ; [$&, :food]
|
88
|
+
end
|
89
|
+
|
90
|
+
os_ptr = /windows|linux|freebsd|solaris|plan b/
|
91
|
+
def_multi :classify, lambda { |s| s =~ os_ptr } do |s|
|
92
|
+
s =~ os_ptr ; [$&, :os]
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
# *** TESTS ***
|
98
|
+
require 'minitest/autorun'
|
99
|
+
|
100
|
+
class TestStuff < MiniTest::Unit::TestCase
|
101
|
+
|
102
|
+
def test_list_pattern_matching
|
103
|
+
pm = ListPatternMatching.new
|
104
|
+
assert_equal([3,2,1], pm.ex1([1, 2, 3]))
|
105
|
+
assert_equal([2,3], pm.ex2([1, 2, 3]))
|
106
|
+
assert_equal([2,3,4], pm.ex3([1, 2, 3, 4]))
|
107
|
+
assert_equal([2,3,4], pm.ex4([1, 2, 3, 4, 5]))
|
108
|
+
assert_equal([2,2,2], pm.ex5([1, [2], [2], [2], 5]))
|
109
|
+
assert_equal(1, pm.ex6([1, 2, 3]))
|
110
|
+
assert_equal([2, 3], pm.ex7([1, [2, 3], 4]))
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_regexp_pattern_matching
|
115
|
+
pm = RegexPatternMatching.new
|
116
|
+
assert_equal(['dog', :animal], pm.classify('some dog'))
|
117
|
+
assert_equal(['-1337', :integer], pm.classify('-1337 or 31337'))
|
118
|
+
assert_equal(['freebsd', :os], pm.classify('freebsd is great!'))
|
119
|
+
assert_equal(['pasta', :food], pm.classify('I love italian pasta'))
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
data/examples/pe_31.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative '../lib/multi_dispatch.rb'
|
2
|
+
|
3
|
+
# PROJECT EULER PROBLEM 31
|
4
|
+
#
|
5
|
+
# The example shows that it's possible to simulate pattern matching
|
6
|
+
# behavior using anonymous functions to define condition for the pattern.
|
7
|
+
# It is equivalent to the haskell cade below:
|
8
|
+
#
|
9
|
+
# count _ 0 = 1
|
10
|
+
# count [c] _ = 1
|
11
|
+
# count (c:cs) s = sum $ map (count cs . (s-)) [0,c..s]
|
12
|
+
|
13
|
+
|
14
|
+
MultiDispatch::def_multi :count, Object, 0 do ; 1 end
|
15
|
+
MultiDispatch::def_multi :count,
|
16
|
+
lambda { |list| list.size == 1 }, Object do |a, b| 1 end
|
17
|
+
MultiDispatch::def_multi :count, Array, Numeric do |list, sum|
|
18
|
+
(0..sum).step(list.first).to_a.map { |e|
|
19
|
+
count(list[1..-1], sum-e)
|
20
|
+
}.reduce(:+)
|
21
|
+
end
|
22
|
+
|
23
|
+
p count( [200,100,50,20,10,5,2,1], 200)
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require_relative 'unbound_multi_method'
|
2
|
+
require_relative 'multi_method'
|
3
|
+
|
4
|
+
module MultiDispatch
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
|
8
|
+
def def_multi(_name, *args, &body)
|
9
|
+
# _name = args.shift
|
10
|
+
# closure (to prevent namespace pollution)
|
11
|
+
multi_methods = {}
|
12
|
+
|
13
|
+
# only at first invocation
|
14
|
+
define_singleton_method :instance_multi_methods do
|
15
|
+
multi_methods.values.flatten
|
16
|
+
end
|
17
|
+
|
18
|
+
define_singleton_method :instance_multi_method do |name, *args|
|
19
|
+
mthd = self.instance_multi_methods.select do |m|
|
20
|
+
m.match?(name, args)
|
21
|
+
end.sort_by { |m| m.match_distance(args) }.first
|
22
|
+
unless mthd
|
23
|
+
raise NoMethodError,
|
24
|
+
"undefined method `#{name}' for class `#{singleton_class}'"
|
25
|
+
end
|
26
|
+
mthd
|
27
|
+
end
|
28
|
+
|
29
|
+
define_singleton_method :def_multi do |name, *args, &body|
|
30
|
+
multi_methods[name] ||= []
|
31
|
+
multi_methods[name].unshift(UnboundMultiMethod.new(name, *args, body ))
|
32
|
+
if !method_defined?(name)
|
33
|
+
define_method name do |*params|
|
34
|
+
singleton_class.instance_multi_method(name, *params).
|
35
|
+
bind(self).call(*params)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
self.send(:def_multi, *([_name]+args), &body)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.def_multi(name, *args, &body)
|
45
|
+
Object.instance_eval do
|
46
|
+
include MultiDispatch
|
47
|
+
def_multi(name, *args, &body)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.instance_multi_methods ; [] end
|
52
|
+
def self.instance_multi_method(name, obj)
|
53
|
+
raise NoMethodError,
|
54
|
+
"undefined method `#{name}' for class `#{obj.class}'"
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module MultiDispatch
|
2
|
+
|
3
|
+
class MultiMethod
|
4
|
+
extend Forwardable
|
5
|
+
|
6
|
+
def_delegators :@unbound_method, :arity, :eql?, :to_s, :owner, :name
|
7
|
+
|
8
|
+
def initialize(obj, unbnd_mthd)
|
9
|
+
@obj, @unbound_method = obj, unbnd_mthd
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(*args)
|
13
|
+
if args.size != arity
|
14
|
+
raise ArgumentError,
|
15
|
+
"wrong number of arguments(#{args.size} for #{arity})"
|
16
|
+
end
|
17
|
+
@obj.instance_exec(*args, &@unbound_method.body)
|
18
|
+
end
|
19
|
+
|
20
|
+
def unbind
|
21
|
+
@unbound_method
|
22
|
+
end
|
23
|
+
|
24
|
+
def receiver
|
25
|
+
@obj
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_proc
|
29
|
+
Proc.new { |*args| call(*args) }
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module MultiDispatch
|
2
|
+
|
3
|
+
class UnboundMultiMethod
|
4
|
+
attr_accessor :patterns, :body, :name
|
5
|
+
|
6
|
+
def initialize(name, *args, body)
|
7
|
+
@name, @patterns, @body = name, args, body
|
8
|
+
end
|
9
|
+
|
10
|
+
def match?(name, params)
|
11
|
+
name == @name &&
|
12
|
+
params.size == arity &&
|
13
|
+
params.zip(@patterns).all? do |param, pattern|
|
14
|
+
# match by type (Class)
|
15
|
+
(pattern.is_a?(Class) && param.kind_of?(pattern)) ||
|
16
|
+
# match by condition passed in Proc object
|
17
|
+
(pattern.is_a?(Proc) && pattern.call(param) rescue false) ||
|
18
|
+
# match by value
|
19
|
+
param == pattern
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def match_distance(params)
|
24
|
+
params.zip(@patterns).reduce(0) do |dist, pair|
|
25
|
+
prm, ptr = pair
|
26
|
+
if prm == ptr ; dist -= 1
|
27
|
+
else
|
28
|
+
if (ptr.is_a?(Class) && prm.kind_of?(ptr))
|
29
|
+
# calculates the distance from given type to parent type
|
30
|
+
prm = prm.class
|
31
|
+
while (dist+=1 ; prm != ptr) do
|
32
|
+
prm = prm.superclass
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
dist
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def bind(obj)
|
41
|
+
MultiMethod.new(obj, self)
|
42
|
+
end
|
43
|
+
|
44
|
+
def arity
|
45
|
+
@patterns.size
|
46
|
+
end
|
47
|
+
|
48
|
+
def eql?(mthd)
|
49
|
+
self.body == mthd.body
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
"#{self.class}: #{self.class}##{name} (#{@patterns.map { |p| p.is_a?(Proc) ? Proc : p }})"
|
54
|
+
end
|
55
|
+
|
56
|
+
def parameters
|
57
|
+
@patterns
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'multi_dispatch/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "multi_dispatch"
|
9
|
+
spec.version = MultiDispatch::VERSION
|
10
|
+
spec.authors = ["Robert Pozoga"]
|
11
|
+
spec.email = ["robert.pozoga@gmail.com"]
|
12
|
+
spec.description = %q{This gem provides light-weight and easy-to-use multiple dispatch generic methods.}
|
13
|
+
spec.summary = %q{Multiple dispatch for Ruby.}
|
14
|
+
spec.homepage = "http://github.com/robpe/multi-dispatch"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files`.split($/)
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
23
|
+
spec.add_development_dependency "rake"
|
24
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require_relative '../lib/multi_dispatch.rb'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
|
4
|
+
class TestAmbiguity < MiniTest::Unit::TestCase
|
5
|
+
|
6
|
+
class Foo
|
7
|
+
include MultiDispatch
|
8
|
+
|
9
|
+
def_multi :arg, 1, Numeric do |*a|
|
10
|
+
a.first
|
11
|
+
end
|
12
|
+
def_multi :arg, Numeric, 2 do |*a|
|
13
|
+
a.last
|
14
|
+
end
|
15
|
+
|
16
|
+
def_multi :is_one?, 1 do
|
17
|
+
true
|
18
|
+
end
|
19
|
+
def_multi :is_one?, lambda { |x| x == 1 } do
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_ambigous_cases
|
26
|
+
f = Foo.new
|
27
|
+
|
28
|
+
assert_equal(2, f.arg(1,2))
|
29
|
+
assert_equal(1, f.arg(1, 1337))
|
30
|
+
assert_equal(2, f.arg(1337, 2))
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_values_over_lambdas
|
34
|
+
f = Foo.new
|
35
|
+
# values have higher priority than lambda functions
|
36
|
+
assert_equal( 1,
|
37
|
+
f.singleton_class.instance_multi_method(:is_one?, 1).parameters.first)
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require_relative '../lib/multi_dispatch'
|
3
|
+
|
4
|
+
|
5
|
+
class TestSimpleStuff < MiniTest::Unit::TestCase
|
6
|
+
|
7
|
+
class JuliaExample
|
8
|
+
include MultiDispatch
|
9
|
+
|
10
|
+
def initialize(mul)
|
11
|
+
@mltplr = mul
|
12
|
+
end
|
13
|
+
|
14
|
+
def_multi :g, Float, Float do |x, y|
|
15
|
+
@mltplr*x + @mltplr*y
|
16
|
+
end
|
17
|
+
|
18
|
+
def_multi :g, Float, Object do |x, y|
|
19
|
+
@mltplr*x + y
|
20
|
+
end
|
21
|
+
|
22
|
+
def_multi :g, Object, Float do |x, y|
|
23
|
+
x + @mltplr*y
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_looking_up_instance_multi_methods_works
|
28
|
+
multi_methods = JuliaExample.instance_multi_methods
|
29
|
+
|
30
|
+
assert_equal(3, multi_methods.size)
|
31
|
+
assert_equal(3, multi_methods.select { |m| m.name == :g }.size)
|
32
|
+
|
33
|
+
example = JuliaExample.new(2)
|
34
|
+
assert_equal(
|
35
|
+
JuliaExample.instance_multi_methods.select { |m|
|
36
|
+
m.patterns.all? { |p| p.eql? Float }
|
37
|
+
}.first,
|
38
|
+
JuliaExample.instance_multi_method(:g, 1.0, 1.0))
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_bind_call_to_proc_methods
|
42
|
+
unbound = JuliaExample.instance_multi_method(:g, Float, Float)
|
43
|
+
bound = unbound.bind(JuliaExample.new(7))
|
44
|
+
|
45
|
+
assert_equal(unbound, bound.unbind)
|
46
|
+
expected = JuliaExample.new(7).g(133.0, 37.0)
|
47
|
+
assert_equal(expected, bound.call(133.0, 37.0))
|
48
|
+
assert_equal(expected, bound.to_proc.call(133.0, 37.0))
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_dispatch_fails_on_nonexistent_methods
|
52
|
+
example = JuliaExample.new(7)
|
53
|
+
assert_raises(NoMethodError) { example.f(1) }
|
54
|
+
assert_raises(NoMethodError) {
|
55
|
+
JuliaExample.instance_multi_method(:f, 1)
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
class TestDispatchFunctionality < MiniTest::Unit::TestCase
|
62
|
+
|
63
|
+
class Container
|
64
|
+
include MultiDispatch
|
65
|
+
|
66
|
+
def_multi :reverse, [] { [] }
|
67
|
+
def_multi :reverse, Array do |list|
|
68
|
+
[list.pop] + reverse(list)
|
69
|
+
end
|
70
|
+
|
71
|
+
def_multi :map, [], Proc do ; [] end
|
72
|
+
def_multi :map, Array, Proc do |list, func|
|
73
|
+
[func.call(list.first)] + map(list[1..-1], func)
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_multi_methods
|
79
|
+
container = Container.new
|
80
|
+
container.reverse([1,2,3,4,5])
|
81
|
+
|
82
|
+
assert_equal( [5,4,3,2,1], container.reverse([1,2,3,4,5]))
|
83
|
+
assert_equal( [1,4,9,16],
|
84
|
+
container.map([1,2,3,4], Proc.new { |x| x**2 }))
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: multi_dispatch
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1dev
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Robert Pozoga
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-09-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: This gem provides light-weight and easy-to-use multiple dispatch generic
|
42
|
+
methods.
|
43
|
+
email:
|
44
|
+
- robert.pozoga@gmail.com
|
45
|
+
executables: []
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- .gitignore
|
50
|
+
- Gemfile
|
51
|
+
- LICENSE
|
52
|
+
- README.md
|
53
|
+
- Rakefile
|
54
|
+
- TODO.md
|
55
|
+
- examples/functional_style.rb
|
56
|
+
- examples/lambda_calc.rb
|
57
|
+
- examples/lc_interpreter.rb
|
58
|
+
- examples/pattern_matching.rb
|
59
|
+
- examples/pe_31.rb
|
60
|
+
- lib/multi_dispatch.rb
|
61
|
+
- lib/multi_dispatch/dispatch.rb
|
62
|
+
- lib/multi_dispatch/multi_method.rb
|
63
|
+
- lib/multi_dispatch/unbound_multi_method.rb
|
64
|
+
- lib/multi_dispatch/version.rb
|
65
|
+
- multi_dispatch.gemspec
|
66
|
+
- tests/ambiguity_tests.rb
|
67
|
+
- tests/multi_dispatch_tests.rb
|
68
|
+
homepage: http://github.com/robpe/multi-dispatch
|
69
|
+
licenses:
|
70
|
+
- MIT
|
71
|
+
metadata: {}
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ! '>='
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ! '>'
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 1.3.1
|
86
|
+
requirements: []
|
87
|
+
rubyforge_project:
|
88
|
+
rubygems_version: 2.0.6
|
89
|
+
signing_key:
|
90
|
+
specification_version: 4
|
91
|
+
summary: Multiple dispatch for Ruby.
|
92
|
+
test_files: []
|
93
|
+
has_rdoc:
|