adt 0.0.6 → 0.0.7
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/lib/adt.rb +41 -54
- data/lib/data/maybe.rb +4 -0
- data/lib/data/validation.rb +21 -0
- metadata +26 -40
data/lib/adt.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
require 'adt/case_recorder'
|
|
2
2
|
|
|
3
|
-
module
|
|
3
|
+
module AdtUtils
|
|
4
4
|
def self.underscore(camel_cased_word)
|
|
5
5
|
word = camel_cased_word.to_s.dup
|
|
6
6
|
word.gsub!(/::/, '/')
|
|
@@ -12,6 +12,16 @@ module StringHelp
|
|
|
12
12
|
end
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
+
def ADT(&block)
|
|
16
|
+
m = Module.new
|
|
17
|
+
m.class.send(:public, :define_method)
|
|
18
|
+
(class <<m; self end).define_method(:extended) do |base|
|
|
19
|
+
base.extend(ADT)
|
|
20
|
+
base.send(:cases, &block)
|
|
21
|
+
end
|
|
22
|
+
m
|
|
23
|
+
end
|
|
24
|
+
|
|
15
25
|
module ADT
|
|
16
26
|
module_function
|
|
17
27
|
|
|
@@ -81,68 +91,64 @@ module ADT
|
|
|
81
91
|
cases = dsl._church_cases
|
|
82
92
|
num_cases = cases.length
|
|
83
93
|
case_names = cases.map { |x| x[0] }
|
|
84
|
-
is_enumeration = cases.all?{ |(_, args)| args.count == 0 }
|
|
85
|
-
|
|
86
|
-
# creates procs with a certain arg count. body should use #{prefix}N to access arguments. The result should be
|
|
87
|
-
# eval'ed at the call site
|
|
88
|
-
proc_create = proc { |argc, prefix, body|
|
|
89
|
-
args = argc > 0 ? "|#{(1..argc).to_a.map { |a| "#{prefix}#{a}" }.join(',')}|" : ""
|
|
90
|
-
"proc { #{args} #{body} }"
|
|
91
|
-
}
|
|
94
|
+
is_enumeration = cases.all? { |(_, args)| args.count == 0 }
|
|
92
95
|
|
|
93
96
|
# Initializer. Should not be used directly.
|
|
94
|
-
define_method(:initialize) do
|
|
95
|
-
@
|
|
97
|
+
define_method(:initialize) do |tag, tag_index, values|
|
|
98
|
+
@tag = tag
|
|
99
|
+
@tag_index = tag_index
|
|
100
|
+
@values = values
|
|
96
101
|
end
|
|
97
102
|
|
|
98
103
|
# The Fold.
|
|
99
104
|
define_method(:fold) do |*args|
|
|
100
105
|
if args.first && args.first.is_a?(Hash) then
|
|
101
|
-
|
|
106
|
+
args.first[@tag].call(*@values)
|
|
102
107
|
else
|
|
103
|
-
@
|
|
108
|
+
args[@tag_index].call(*@values)
|
|
104
109
|
end
|
|
105
110
|
end
|
|
106
111
|
|
|
107
112
|
# If we're inside a named class, then set up an alias to fold
|
|
108
|
-
fold_synonym =
|
|
109
|
-
if fold_synonym && fold_synonym.
|
|
110
|
-
|
|
113
|
+
fold_synonym = name && AdtUtils.underscore(name.split('::').last)
|
|
114
|
+
if fold_synonym && !fold_synonym.empty? then
|
|
115
|
+
alias_method(fold_synonym, :fold)
|
|
111
116
|
end
|
|
112
117
|
|
|
113
118
|
# The Constructors
|
|
114
119
|
cases.each_with_index do |(name, case_args), index|
|
|
115
|
-
constructor =
|
|
120
|
+
constructor = lambda { |*args| self.new(name, index, args) }
|
|
116
121
|
if case_args.size > 0 then
|
|
117
122
|
singleton_class.send(:define_method, name, &constructor)
|
|
118
123
|
else
|
|
119
124
|
# Cache the constructed value if it is unary
|
|
120
125
|
singleton_class.send(:define_method, name) do
|
|
121
126
|
instance_variable_get("@#{name}") || begin
|
|
122
|
-
instance_variable_set("@#{name}",
|
|
127
|
+
instance_variable_set("@#{name}", self.new(name, index, []))
|
|
123
128
|
end
|
|
124
129
|
end
|
|
125
130
|
end
|
|
126
131
|
end
|
|
127
132
|
|
|
133
|
+
# Case info
|
|
134
|
+
singleton_class.send(:define_method, :case_info) { cases }
|
|
135
|
+
|
|
128
136
|
# Getter methods for common accessors
|
|
129
137
|
all_arg_names = cases.map { |(_, args)| args }.flatten
|
|
130
138
|
all_arg_names.each do |arg|
|
|
131
|
-
case_positions = cases.map { |(
|
|
139
|
+
case_positions = cases.map { |(n, args)| args.index(arg) }
|
|
132
140
|
if case_positions.all?
|
|
133
|
-
define_method(arg)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
})
|
|
137
|
-
end
|
|
141
|
+
define_method(arg) {
|
|
142
|
+
@values[case_positions[@tag_index]]
|
|
143
|
+
}
|
|
138
144
|
end
|
|
139
145
|
end
|
|
140
146
|
|
|
141
147
|
# Case info methods
|
|
142
148
|
# Indexing is 1-based
|
|
143
|
-
define_method(:case_index)
|
|
144
|
-
define_method(:case_name)
|
|
145
|
-
define_method(:case_arity)
|
|
149
|
+
define_method(:case_index) { @tag_index + 1 }
|
|
150
|
+
define_method(:case_name) { @tag.to_s }
|
|
151
|
+
define_method(:case_arity) { self.class.case_info[@tag_index][1].count }
|
|
146
152
|
|
|
147
153
|
# Enumerations are defined as classes with cases that don't take arguments. A number of useful
|
|
148
154
|
# functions can be defined for these.
|
|
@@ -151,31 +157,22 @@ module ADT
|
|
|
151
157
|
@all_values ||= case_names.map { |x| send(x) }
|
|
152
158
|
end
|
|
153
159
|
|
|
154
|
-
|
|
160
|
+
alias_method(:to_i, :case_index)
|
|
155
161
|
singleton_class.send(:define_method, :from_i) do |idx| send(case_names[idx - 1]) end
|
|
162
|
+
#TODO succ, pred
|
|
156
163
|
end
|
|
157
164
|
|
|
158
165
|
# The usual object helpers
|
|
159
166
|
define_method(:inspect) do
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
bit = case_args.map { |ca|
|
|
163
|
-
index += 1
|
|
164
|
-
" #{ca}:#\{a#{index}\.inspect}"
|
|
165
|
-
}.join('')
|
|
166
|
-
eval(proc_create[case_args.count, "a", " \" #{cn}#{bit}\""])
|
|
167
|
-
}) + ">"
|
|
167
|
+
args = self.class.case_info[@tag_index][1]
|
|
168
|
+
"#<#{self.class.name} #{@tag}#{args.zip(@values).map { |(x,y)| " #{x}:#{y.inspect}" }.join("")}>"
|
|
168
169
|
end
|
|
169
170
|
|
|
170
171
|
define_method(:==) do |other|
|
|
171
172
|
!other.nil? && case_index == other.case_index && to_a == other.to_a
|
|
172
173
|
end
|
|
173
174
|
|
|
174
|
-
define_method(:to_a)
|
|
175
|
-
fold(*cases.map { |(cn, args)|
|
|
176
|
-
eval(proc_create[args.count, "a", "[" + (1..args.count).to_a.map { |idx| "a#{idx}" }.join(',') + "]"])
|
|
177
|
-
})
|
|
178
|
-
end
|
|
175
|
+
define_method(:to_a) { @values }
|
|
179
176
|
|
|
180
177
|
# Comparisons are done by index, then by the values within the case (if any) via #to_a
|
|
181
178
|
define_method(:<=>) do |other|
|
|
@@ -190,22 +187,12 @@ module ADT
|
|
|
190
187
|
cases.each_with_index do |(name, args), idx|
|
|
191
188
|
# Thing.foo(5).foo? # <= true
|
|
192
189
|
# Thing.foo(5).bar? # <= false
|
|
193
|
-
define_method("#{name}?")
|
|
194
|
-
fold(*case_names.map { |cn|
|
|
195
|
-
eval(proc_create[0, "a", cn == name ? "true" : "false"])
|
|
196
|
-
})
|
|
197
|
-
end
|
|
190
|
+
define_method("#{name}?") { @tag == name }
|
|
198
191
|
|
|
199
192
|
# Thing.foo(5).when_foo(proc {|v| v }, proc { 0 }) # <= 5
|
|
200
193
|
# Thing.bar(5).when_foo(proc {|v| v }, proc { 0 }) # <= 0
|
|
201
194
|
define_method("when_#{name}") do |handle, default|
|
|
202
|
-
|
|
203
|
-
if (cn == name)
|
|
204
|
-
proc { |*args| handle.call(*args) }
|
|
205
|
-
else
|
|
206
|
-
default
|
|
207
|
-
end
|
|
208
|
-
})
|
|
195
|
+
@tag == name ? handle.call(*@values) : default.call
|
|
209
196
|
end
|
|
210
197
|
end
|
|
211
198
|
end
|
|
@@ -249,7 +236,7 @@ module ADT
|
|
|
249
236
|
# instance_exec it back on the instance.
|
|
250
237
|
# TODO: use the proc builder like in the `cases` method, which will let us tie
|
|
251
238
|
# down the arity
|
|
252
|
-
some_impl =
|
|
239
|
+
some_impl = lambda { |*args| the_instance.instance_exec(*args, &impl) }
|
|
253
240
|
memo[c] = some_impl
|
|
254
241
|
memo
|
|
255
242
|
})
|
data/lib/data/maybe.rb
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'adt'
|
|
2
|
+
|
|
3
|
+
class Validation
|
|
4
|
+
extend ADT
|
|
5
|
+
cases do
|
|
6
|
+
failure(:errors)
|
|
7
|
+
success(:value)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.fail_with(error)
|
|
11
|
+
failure([error])
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def map
|
|
15
|
+
fold(proc { |_| self }, proc { |v| Validation.success(yield v) })
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def bind(&b)
|
|
19
|
+
fold(proc { |_| self }, b)
|
|
20
|
+
end
|
|
21
|
+
end
|
metadata
CHANGED
|
@@ -1,66 +1,52 @@
|
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: adt
|
|
3
|
-
version: !ruby/object:Gem::Version
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
- 0
|
|
7
|
-
- 0
|
|
8
|
-
- 6
|
|
9
|
-
version: 0.0.6
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.7
|
|
5
|
+
prerelease:
|
|
10
6
|
platform: ruby
|
|
11
|
-
authors:
|
|
7
|
+
authors:
|
|
12
8
|
- Nick Partridge
|
|
13
9
|
autorequire:
|
|
14
10
|
bindir: bin
|
|
15
11
|
cert_chain: []
|
|
16
|
-
|
|
17
|
-
date: 2011-09-06 00:00:00 +10:00
|
|
18
|
-
default_executable:
|
|
12
|
+
date: 2012-07-10 00:00:00.000000000 Z
|
|
19
13
|
dependencies: []
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
description: Define multiple constructors for a type, then match on them! Get all
|
|
15
|
+
catamorphic.
|
|
22
16
|
email: nkpart@gmail.com
|
|
23
17
|
executables: []
|
|
24
|
-
|
|
25
18
|
extensions: []
|
|
26
|
-
|
|
27
19
|
extra_rdoc_files: []
|
|
28
|
-
|
|
29
|
-
files:
|
|
20
|
+
files:
|
|
30
21
|
- lib/adt/case_recorder.rb
|
|
31
22
|
- lib/adt.rb
|
|
32
23
|
- lib/data/maybe.rb
|
|
24
|
+
- lib/data/validation.rb
|
|
33
25
|
- CHANGELOG.md
|
|
34
26
|
- README.md
|
|
35
|
-
|
|
36
|
-
homepage: ""
|
|
27
|
+
homepage: ''
|
|
37
28
|
licenses: []
|
|
38
|
-
|
|
39
29
|
post_install_message:
|
|
40
30
|
rdoc_options: []
|
|
41
|
-
|
|
42
|
-
require_paths:
|
|
31
|
+
require_paths:
|
|
43
32
|
- lib
|
|
44
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
requirements:
|
|
53
|
-
- -
|
|
54
|
-
- !ruby/object:Gem::Version
|
|
55
|
-
|
|
56
|
-
- 0
|
|
57
|
-
version: "0"
|
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
34
|
+
none: false
|
|
35
|
+
requirements:
|
|
36
|
+
- - ! '>='
|
|
37
|
+
- !ruby/object:Gem::Version
|
|
38
|
+
version: '0'
|
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
40
|
+
none: false
|
|
41
|
+
requirements:
|
|
42
|
+
- - ! '>='
|
|
43
|
+
- !ruby/object:Gem::Version
|
|
44
|
+
version: '0'
|
|
58
45
|
requirements: []
|
|
59
|
-
|
|
60
46
|
rubyforge_project:
|
|
61
|
-
rubygems_version: 1.
|
|
47
|
+
rubygems_version: 1.8.11
|
|
62
48
|
signing_key:
|
|
63
49
|
specification_version: 3
|
|
64
50
|
summary: Algebraic data type DSL
|
|
65
51
|
test_files: []
|
|
66
|
-
|
|
52
|
+
has_rdoc:
|