multi_dispatch 0.1dev
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.
- 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:
|