algebrick 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -4
- data/README_FULL.md +3 -3
- data/VERSION +1 -0
- data/lib/algebrick.rb +40 -10
- data/spec/algebrick.rb +541 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 986e5746d10e9a2e2edf3c25da212fff33719dee
|
4
|
+
data.tar.gz: a711d19897aa1c4085948aa21cace8a86ede5ef6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c0264da1e050dab04f2238905fab10b253762c68bcaa3c80103a6c4366efb236c003c3b8ce06cba03b0a0a7913f63662bf9246181237215881c99c77856d14a
|
7
|
+
data.tar.gz: 52d55de9dd00eebb7b6e38959cbf8bae0add19090ff04213a58a053f30389ef313669c41831848d5bf942f6a11274e4c1729f21dbd3b77256d1f4b2c02cac1dc
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
## Algebrick
|
2
2
|
|
3
|
-
|
3
|
+
Is a small gem providing algebraic types and pattern matching on them for Ruby.
|
4
4
|
|
5
|
-
- Documentation: http://blog.pitr.ch/algebrick
|
6
|
-
- Source: https://github.com/pitr-ch/algebrick
|
7
|
-
- Blog: http://blog.pitr.ch/blog/categories/algebrick
|
5
|
+
- Documentation: <http://blog.pitr.ch/algebrick>
|
6
|
+
- Source: <https://github.com/pitr-ch/algebrick>
|
7
|
+
- Blog: <http://blog.pitr.ch/blog/categories/algebrick/>
|
data/README_FULL.md
CHANGED
@@ -2,9 +2,9 @@
|
|
2
2
|
|
3
3
|
Is a small gem providing algebraic types and pattern matching on them for Ruby.
|
4
4
|
|
5
|
-
- Documentation:
|
6
|
-
- Source:
|
7
|
-
- Blog:
|
5
|
+
- Documentation: <http://blog.pitr.ch/algebrick>
|
6
|
+
- Source: <https://github.com/pitr-ch/algebrick>
|
7
|
+
- Blog: <http://blog.pitr.ch/blog/categories/algebrick/>
|
8
8
|
|
9
9
|
## Quick example with Maybe type
|
10
10
|
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.2
|
data/lib/algebrick.rb
CHANGED
@@ -13,6 +13,10 @@ end
|
|
13
13
|
|
14
14
|
module Algebrick
|
15
15
|
|
16
|
+
def self.version
|
17
|
+
@version ||= Gem::Version.new File.read(File.join(File.dirname(__FILE__), '..', 'VERSION'))
|
18
|
+
end
|
19
|
+
|
16
20
|
module TypeCheck
|
17
21
|
#def is_kind_of?(value, *types)
|
18
22
|
# a_type_check :kind_of?, false, value, *types
|
@@ -22,9 +26,9 @@ module Algebrick
|
|
22
26
|
a_type_check :kind_of?, true, value, *types
|
23
27
|
end
|
24
28
|
|
25
|
-
|
26
|
-
|
27
|
-
|
29
|
+
def is_matching?(value, *types)
|
30
|
+
a_type_check :===, false, value, *types
|
31
|
+
end
|
28
32
|
|
29
33
|
def is_matching!(value, *types)
|
30
34
|
a_type_check :===, true, value, *types
|
@@ -43,8 +47,10 @@ module Algebrick
|
|
43
47
|
raise ArgumentError
|
44
48
|
end
|
45
49
|
end
|
46
|
-
|
47
|
-
|
50
|
+
bang && !ok and
|
51
|
+
raise TypeError,
|
52
|
+
"value (#{value.class}) '#{value}' is not ##{which} any of #{types.join(', ')}"
|
53
|
+
bang ? value : ok
|
48
54
|
end
|
49
55
|
end
|
50
56
|
|
@@ -83,7 +89,7 @@ module Algebrick
|
|
83
89
|
cases.each do |matcher, block|
|
84
90
|
return match_value matcher, block if matcher === value
|
85
91
|
end
|
86
|
-
raise "no match for #{value} by any of #{cases.map(&:first).join ', '}"
|
92
|
+
raise "no match for (#{value.class}) '#{value}' by any of #{cases.map(&:first).join ', '}"
|
87
93
|
end
|
88
94
|
|
89
95
|
private
|
@@ -307,6 +313,20 @@ module Algebrick
|
|
307
313
|
self
|
308
314
|
end
|
309
315
|
|
316
|
+
def add_field_method_accessor(field)
|
317
|
+
raise TypeError, 'no field names' unless @field_names
|
318
|
+
raise TypeError, "no field name #{field}" unless @field_names.include? field
|
319
|
+
define_method(field) { self[field] }
|
320
|
+
end
|
321
|
+
|
322
|
+
def add_field_method_accessors(*fields)
|
323
|
+
fields.each { |f| add_field_method_accessor f }
|
324
|
+
end
|
325
|
+
|
326
|
+
def add_all_field_method_accessors
|
327
|
+
add_field_method_accessors *@field_names
|
328
|
+
end
|
329
|
+
|
310
330
|
protected
|
311
331
|
|
312
332
|
def be_kind_of!(type)
|
@@ -336,7 +356,9 @@ module Algebrick
|
|
336
356
|
if keys
|
337
357
|
@field_names = keys
|
338
358
|
keys.all? { |k| is_kind_of! k, Symbol }
|
339
|
-
dict = @field_indexes =
|
359
|
+
dict = @field_indexes =
|
360
|
+
Hash.new { |h, k| raise ArgumentError, "uknown field #{k.inspect}" }.
|
361
|
+
update keys.each_with_index.inject({}) { |h, (k, i)| h.update k => i }
|
340
362
|
define_method(:[]) { |key| @fields[dict[key]] }
|
341
363
|
end
|
342
364
|
|
@@ -551,6 +573,8 @@ module Algebrick
|
|
551
573
|
|
552
574
|
alias_method :>>, :-
|
553
575
|
|
576
|
+
raise 'remove deprecation' if Algebrick.version >= Gem::Version.new('0.2')
|
577
|
+
|
554
578
|
def +(block)
|
555
579
|
warn 'a_matcher +-> {} is deprecated, it\'ll be removed in 0.2'
|
556
580
|
self - block
|
@@ -678,7 +702,7 @@ module Algebrick
|
|
678
702
|
end
|
679
703
|
end
|
680
704
|
|
681
|
-
class Not < Abstract
|
705
|
+
class Not < Abstract
|
682
706
|
attr_reader :matcher
|
683
707
|
|
684
708
|
def initialize(matcher)
|
@@ -1012,8 +1036,14 @@ module Algebrick
|
|
1012
1036
|
end
|
1013
1037
|
end
|
1014
1038
|
|
1015
|
-
def type_def(&definition)
|
1016
|
-
Environment.new(
|
1039
|
+
def type_def(base = self, &definition)
|
1040
|
+
Environment.new(base, &definition).run
|
1017
1041
|
end
|
1018
1042
|
end
|
1043
|
+
|
1044
|
+
extend DSL
|
1045
|
+
|
1046
|
+
def self.type_def(base, &definition)
|
1047
|
+
super base, &definition
|
1048
|
+
end
|
1019
1049
|
end
|
data/spec/algebrick.rb
ADDED
@@ -0,0 +1,541 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
|
3
|
+
require 'minitest/spec'
|
4
|
+
#require 'minitest/autorun'
|
5
|
+
require 'turn/autorun'
|
6
|
+
|
7
|
+
require 'pp'
|
8
|
+
require_relative '../lib/algebrick'
|
9
|
+
require 'pry'
|
10
|
+
|
11
|
+
class Module
|
12
|
+
def const_missing const
|
13
|
+
raise "no constant #{const.inspect} in #{self}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Turn.config do |c|
|
18
|
+
# use one of output formats:
|
19
|
+
# :outline - turn's original case/test outline mode [default]
|
20
|
+
# :progress - indicates progress with progress bar
|
21
|
+
# :dotted - test/unit's traditional dot-progress mode
|
22
|
+
# :pretty - new pretty reporter
|
23
|
+
# :marshal - dump output as YAML (normal run mode only)
|
24
|
+
# :cue - interactive testing
|
25
|
+
c.format = :outline
|
26
|
+
# turn on invoke/execute tracing, enable full backtrace
|
27
|
+
#c.trace = true
|
28
|
+
# use humanized test names (works only with :outline format)
|
29
|
+
c.natural = true
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
describe 'AlgebrickTest' do
|
34
|
+
i_suck_and_my_tests_are_order_dependent!
|
35
|
+
|
36
|
+
#type_def do
|
37
|
+
# maybe === none | some(Object)
|
38
|
+
|
39
|
+
# message === success | failure
|
40
|
+
# sucess === int(Integer) | str(String)
|
41
|
+
# failure === error | str_error(String)
|
42
|
+
|
43
|
+
#user = user(String, address) do
|
44
|
+
# def name
|
45
|
+
# fields[0]
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# def address
|
49
|
+
# fields[1]
|
50
|
+
# end
|
51
|
+
#end
|
52
|
+
#
|
53
|
+
#psc = psc(String) do
|
54
|
+
# REG = /\d{5}/
|
55
|
+
#
|
56
|
+
# def initialize(*fields)
|
57
|
+
# raise TypeError unless fields[0] =~ REG
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# def schema
|
61
|
+
# super << { pattern: REG.to_s }
|
62
|
+
# end
|
63
|
+
#end
|
64
|
+
#
|
65
|
+
## street, num, city
|
66
|
+
#address = address(street(String), street_number(Integer), city(String), psc)
|
67
|
+
#
|
68
|
+
#message1 === user
|
69
|
+
#
|
70
|
+
#User['pepa',
|
71
|
+
# Adress[
|
72
|
+
# Street['Stavebni'],
|
73
|
+
# StreetNumber[5],
|
74
|
+
# City['Brno']]]
|
75
|
+
#
|
76
|
+
#{ user: ['pepa',
|
77
|
+
# { address: [{ street: 'Stavebni' },
|
78
|
+
# { street_number: 5 }] }] }
|
79
|
+
|
80
|
+
#sucess === int(Integer) | str(String)
|
81
|
+
#failure === error | str_error(String)
|
82
|
+
#message === success | failure
|
83
|
+
|
84
|
+
|
85
|
+
#list === empty | list_item(Integer, list)
|
86
|
+
#end
|
87
|
+
|
88
|
+
#Empty = Algebrick::Atom.new
|
89
|
+
#Leaf = Algebrick::Product.new(Integer)
|
90
|
+
#Tree = Algebrick::Variant.allocate
|
91
|
+
#Node = Algebrick::Product.new(Tree, Tree)
|
92
|
+
#Tree.send :initialize, Empty, Leaf, Node do
|
93
|
+
# def a
|
94
|
+
# :a
|
95
|
+
# end
|
96
|
+
#end
|
97
|
+
#List = Algebrick::ProductVariant.allocate
|
98
|
+
#List.send :initialize, [Integer, List], [Empty, List]
|
99
|
+
|
100
|
+
extend Algebrick::DSL
|
101
|
+
|
102
|
+
type_def do
|
103
|
+
tree === empty | leaf(Integer) | node(tree, tree)
|
104
|
+
tree do
|
105
|
+
def a
|
106
|
+
:a
|
107
|
+
end
|
108
|
+
|
109
|
+
def depth
|
110
|
+
case self
|
111
|
+
when Empty
|
112
|
+
0
|
113
|
+
when Leaf
|
114
|
+
1
|
115
|
+
when Node
|
116
|
+
left, right = *self
|
117
|
+
1 + [left.depth, right.depth].max
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def each(&block)
|
122
|
+
return to_enum :each unless block
|
123
|
+
case self
|
124
|
+
when Empty
|
125
|
+
when Leaf
|
126
|
+
block.call self.value
|
127
|
+
when Node
|
128
|
+
left, right = *self
|
129
|
+
left.each &block
|
130
|
+
right.each &block
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def sum
|
135
|
+
each.inject(0) { |sum, v| sum + v }
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
list === empty | list(Integer, list)
|
140
|
+
end
|
141
|
+
|
142
|
+
Empty = self::Empty
|
143
|
+
Node = self::Node
|
144
|
+
Leaf = self::Leaf
|
145
|
+
Tree = self::Tree
|
146
|
+
List = self::List
|
147
|
+
|
148
|
+
describe 'type definition' do
|
149
|
+
module Asd
|
150
|
+
Algebrick.type_def self do
|
151
|
+
b === c | d
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'asd' do
|
156
|
+
assert Asd::B
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
describe 'type.to_s' do
|
161
|
+
it { Empty.to_s.must_equal 'Empty' }
|
162
|
+
it { Node.to_s.must_equal 'Node(Tree, Tree)' }
|
163
|
+
it { Leaf.to_s.must_equal 'Leaf(Integer)' }
|
164
|
+
it { Tree.to_s.must_equal 'Tree(Empty | Leaf | Node)' }
|
165
|
+
it { List.to_s.must_equal 'List(Empty | List(Integer, List))' }
|
166
|
+
end
|
167
|
+
|
168
|
+
describe 'atom' do
|
169
|
+
it { Empty.must_be_kind_of Algebrick::Type }
|
170
|
+
it { Empty.must_be_kind_of Algebrick::Value }
|
171
|
+
it { assert Empty.kind_of? Empty }
|
172
|
+
|
173
|
+
it { assert Empty == Empty }
|
174
|
+
it { assert Empty === Empty }
|
175
|
+
it { eval(Empty.to_s).must_equal Empty }
|
176
|
+
it { eval(Empty.inspect).must_equal Empty }
|
177
|
+
|
178
|
+
it { Empty.from_hash(Empty.to_hash).must_equal Empty }
|
179
|
+
end
|
180
|
+
|
181
|
+
describe 'product' do
|
182
|
+
it { Leaf[1].must_be_kind_of Algebrick::Value }
|
183
|
+
it { Leaf.must_be_kind_of Algebrick::Type }
|
184
|
+
it { Leaf[1].wont_be_kind_of Algebrick::Type }
|
185
|
+
it { Leaf.wont_be_kind_of Algebrick::Value }
|
186
|
+
|
187
|
+
it { assert Leaf[1] == Leaf[1] }
|
188
|
+
it { assert Leaf[1] != Leaf[0] }
|
189
|
+
it { assert Leaf === Leaf[1] }
|
190
|
+
it { assert Leaf[1].kind_of? Leaf }
|
191
|
+
it { eval(Leaf[1].to_s).must_equal Leaf[1] }
|
192
|
+
it { eval(Leaf[1].inspect).must_equal Leaf[1] }
|
193
|
+
it { eval(Node[Leaf[1], Empty].to_s).must_equal Node[Leaf[1], Empty] }
|
194
|
+
it { eval(Node[Leaf[1], Empty].inspect).must_equal Node[Leaf[1], Empty] }
|
195
|
+
|
196
|
+
it 'field assign' do
|
197
|
+
value = Leaf[1].value
|
198
|
+
value.must_equal 1
|
199
|
+
|
200
|
+
left, right = *Node[Empty, Leaf[1]]
|
201
|
+
left.must_equal Empty
|
202
|
+
right.must_equal Leaf[1]
|
203
|
+
|
204
|
+
lambda { Node[Empty, Empty].value }.must_raise NoMethodError
|
205
|
+
end
|
206
|
+
|
207
|
+
it { lambda { Leaf['a'] }.must_raise TypeError }
|
208
|
+
it { lambda { Leaf[nil] }.must_raise TypeError }
|
209
|
+
it { lambda { Node['a'] }.must_raise TypeError }
|
210
|
+
it { lambda { Node[Empty, nil] }.must_raise TypeError }
|
211
|
+
|
212
|
+
describe 'named field' do
|
213
|
+
type_def { named(a: Integer, b: Object) }
|
214
|
+
Named = self::Named
|
215
|
+
Named.add_all_field_method_accessors
|
216
|
+
it { -> { Named[:a, 1] }.must_raise TypeError }
|
217
|
+
it { Named[1, :a][:a].must_equal 1 }
|
218
|
+
it { Named[1, :a][:b].must_equal :a }
|
219
|
+
it { Named[a: 1, b: :a][:a].must_equal 1 }
|
220
|
+
it { Named[b: :a, a: 1][:a].must_equal 1 }
|
221
|
+
it { Named[a: 1, b: :a][:b].must_equal :a }
|
222
|
+
it { Named[a: 1, b: 2].to_s.must_equal 'Named[a: 1, b: 2]' }
|
223
|
+
it { Named[a: 1, b: 2].a.must_equal 1 }
|
224
|
+
it { Named[a: 1, b: 2].b.must_equal 2 }
|
225
|
+
end
|
226
|
+
|
227
|
+
it { Leaf.from_hash(Leaf[1].to_hash).must_equal Leaf[1] }
|
228
|
+
it { Named.from_hash(Named[1, :a].to_hash).must_equal Named[1, :a] }
|
229
|
+
it { Named[1, Leaf[1]].to_hash.must_equal 'Named' => { a: 1, b: { 'Leaf' => [1] } } }
|
230
|
+
it { Named.from_hash(Named[1, Leaf[1]].to_hash).must_equal Named[1, Leaf[1]] }
|
231
|
+
|
232
|
+
end
|
233
|
+
|
234
|
+
describe 'variant' do
|
235
|
+
it { Tree.must_be_kind_of Algebrick::Type }
|
236
|
+
it { Empty.must_be_kind_of Tree }
|
237
|
+
it { Empty.a.must_equal :a }
|
238
|
+
it { Leaf[1].must_be_kind_of Tree }
|
239
|
+
it { Leaf[1].a.must_equal :a }
|
240
|
+
it { Node[Empty, Empty].must_be_kind_of Tree }
|
241
|
+
#it { assert Empty.kind_of? List }
|
242
|
+
|
243
|
+
it { assert Tree === Empty }
|
244
|
+
it { assert Tree === Leaf[1] }
|
245
|
+
|
246
|
+
describe 'inherit behavior deep' do
|
247
|
+
type_def do
|
248
|
+
a === a1 | a2
|
249
|
+
a1 === b1 | b2
|
250
|
+
a do
|
251
|
+
def a
|
252
|
+
:a
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
it { self.class::B1.a.must_equal :a }
|
258
|
+
end
|
259
|
+
|
260
|
+
describe 'a klass as a variant' do
|
261
|
+
MaybeString = Algebrick::Variant.new Empty, String
|
262
|
+
it { 'a'.must_be_kind_of MaybeString }
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
describe 'product_variant' do
|
267
|
+
it { List[1, Empty].must_be_kind_of Algebrick::Value }
|
268
|
+
it { List.must_be_kind_of Algebrick::Type }
|
269
|
+
|
270
|
+
it { List[1, Empty].must_be_kind_of List }
|
271
|
+
it { List[1, List[1, Empty]].must_be_kind_of List }
|
272
|
+
it { Empty.must_be_kind_of List }
|
273
|
+
|
274
|
+
it { assert List[1, Empty] == List[1, Empty] }
|
275
|
+
it { assert List[1, Empty] != List[2, Empty] }
|
276
|
+
it { assert List === List[1, Empty] }
|
277
|
+
it { assert List === Empty }
|
278
|
+
it { assert List[1, Empty].kind_of? List }
|
279
|
+
end
|
280
|
+
|
281
|
+
describe '#depth' do
|
282
|
+
it do
|
283
|
+
tree = Node[Node[Empty, Leaf[1]], Leaf[1]]
|
284
|
+
tree.depth.must_equal 3
|
285
|
+
end
|
286
|
+
it do
|
287
|
+
tree = Node[Empty, Leaf[1]]
|
288
|
+
tree.depth.must_equal 2
|
289
|
+
end
|
290
|
+
it do
|
291
|
+
tree = Empty
|
292
|
+
tree.depth.must_equal 0
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
describe '#sum' do
|
297
|
+
it do
|
298
|
+
tree = Node[Node[Empty, Leaf[1]], Leaf[1]]
|
299
|
+
tree.sum.must_equal 2
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
describe 'maybe' do
|
304
|
+
type_def do
|
305
|
+
maybe === none | some(Object)
|
306
|
+
maybe do
|
307
|
+
def maybe(&block)
|
308
|
+
case self
|
309
|
+
when None
|
310
|
+
when Some
|
311
|
+
block.call self.value
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
None = self::None
|
318
|
+
Some = self::Some
|
319
|
+
|
320
|
+
it { refute None.maybe { true } }
|
321
|
+
it { assert Some[nil].maybe { true } }
|
322
|
+
end
|
323
|
+
|
324
|
+
extend Algebrick::Matching
|
325
|
+
include Algebrick::Matching
|
326
|
+
|
327
|
+
describe 'matchers' do
|
328
|
+
it 'assigns' do
|
329
|
+
m = ~Empty
|
330
|
+
m === 2
|
331
|
+
m.assigns.must_equal [nil]
|
332
|
+
m === Empty
|
333
|
+
m.assigns.must_equal [Empty]
|
334
|
+
|
335
|
+
m = ~String.to_m
|
336
|
+
m === 2
|
337
|
+
m.assigns.must_equal [nil]
|
338
|
+
m === 'a'
|
339
|
+
m.assigns.must_equal %w(a)
|
340
|
+
|
341
|
+
m = ~Leaf.(~any)
|
342
|
+
m === Leaf[5]
|
343
|
+
m.assigns.must_equal [Leaf[5], 5]
|
344
|
+
m === Leaf[3]
|
345
|
+
m.assigns.must_equal [Leaf[3], 3]
|
346
|
+
end
|
347
|
+
|
348
|
+
it 'assigns in case' do
|
349
|
+
case Leaf[5]
|
350
|
+
when m = ~Leaf.(~any)
|
351
|
+
m.assigns.must_equal [Leaf[5], 5]
|
352
|
+
m.assigns do |leaf, value|
|
353
|
+
leaf.must_equal Leaf[5]
|
354
|
+
value.must_equal 5
|
355
|
+
end
|
356
|
+
else
|
357
|
+
raise
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
describe 'match' do
|
362
|
+
it 'returns value from executed block' do
|
363
|
+
Algebrick.match(Empty, Empty.to_m.case { 1 }).must_equal 1
|
364
|
+
end
|
365
|
+
|
366
|
+
it 'passes assigned values' do
|
367
|
+
v = Algebrick.match Leaf[5],
|
368
|
+
Leaf.(~any).case { |value| value }
|
369
|
+
v.must_equal 5
|
370
|
+
|
371
|
+
v = Algebrick.match Leaf[5],
|
372
|
+
Leaf.(~any) => -> value { value }
|
373
|
+
v.must_equal 5
|
374
|
+
end
|
375
|
+
|
376
|
+
it 'raises when no match' do
|
377
|
+
-> { Algebrick.match Empty,
|
378
|
+
Leaf.(any).case {} }.must_raise RuntimeError
|
379
|
+
end
|
380
|
+
|
381
|
+
it 'does not pass any values when no matcher' do
|
382
|
+
Algebrick.match(Empty, Empty => -> *a { a }).must_equal []
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
describe '#to_s' do
|
387
|
+
[Empty.to_m,
|
388
|
+
# leaf(Object)
|
389
|
+
~Leaf.(Integer),
|
390
|
+
~Empty.to_m,
|
391
|
+
any,
|
392
|
+
~any,
|
393
|
+
Leaf.(any),
|
394
|
+
~Leaf.(any),
|
395
|
+
Node.(Leaf.(any), any),
|
396
|
+
~Node.(Leaf.(any), any),
|
397
|
+
~Leaf.(1) | Leaf.(~any),
|
398
|
+
~Leaf.(1) & Leaf.(~any)
|
399
|
+
].each do |matcher|
|
400
|
+
it matcher.to_s do
|
401
|
+
eval(matcher.to_s).must_equal matcher
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
{ Empty.to_m => Empty,
|
407
|
+
any => Empty,
|
408
|
+
any => Leaf[1],
|
409
|
+
|
410
|
+
Empty => Empty,
|
411
|
+
Empty.to_m => Empty,
|
412
|
+
|
413
|
+
Leaf => Leaf[1],
|
414
|
+
Leaf.(any) => Leaf[5],
|
415
|
+
Leaf.(~any) => Leaf[5],
|
416
|
+
|
417
|
+
Node => Node[Empty, Empty],
|
418
|
+
Node.(any, any) => Node[Leaf[1], Empty],
|
419
|
+
Node.(Empty, any) => Node[Empty, Leaf[1]],
|
420
|
+
Node.(Leaf.(any), any) => Node[Leaf[1], Empty],
|
421
|
+
Node.(Leaf.(any), any) => Node[Leaf[1], Empty],
|
422
|
+
|
423
|
+
Tree.to_m => Node[Leaf[1], Empty],
|
424
|
+
Tree.to_m => Node[Leaf[1], Empty],
|
425
|
+
Node => Node[Leaf[1], Empty],
|
426
|
+
|
427
|
+
Tree & Leaf.(_) => Leaf[1],
|
428
|
+
Empty | Leaf.(_) => Leaf[1],
|
429
|
+
Empty | Leaf.(_) => Empty,
|
430
|
+
!Empty & Leaf.(_) => Leaf[1],
|
431
|
+
Empty & !Leaf.(_) => Empty,
|
432
|
+
|
433
|
+
Array.() => [],
|
434
|
+
Array.(1) => [1],
|
435
|
+
Array.(Empty, Leaf.(-> v { v > 0 })) => [Empty, Leaf[1]],
|
436
|
+
Array.(TrueClass) => [true],
|
437
|
+
|
438
|
+
}.each do |matcher, value|
|
439
|
+
it "#{matcher} === #{value}" do
|
440
|
+
assert matcher === value
|
441
|
+
end
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
it {
|
446
|
+
assert List.to_m === Empty
|
447
|
+
assert List === Empty
|
448
|
+
assert List.to_m === List[1, Empty]
|
449
|
+
assert List === List[1, Empty]
|
450
|
+
assert List.(1, _) === List[1, Empty]
|
451
|
+
refute List.(_, _) === Empty
|
452
|
+
}
|
453
|
+
|
454
|
+
describe 'and-or matching' do
|
455
|
+
it do
|
456
|
+
m = ~Leaf.(1) | ~Leaf.(~any)
|
457
|
+
assert m === Leaf[1]
|
458
|
+
m.assigns.must_equal [Leaf[1], nil, nil]
|
459
|
+
end
|
460
|
+
it do
|
461
|
+
m = ~Leaf.(1) | ~Leaf.(~any)
|
462
|
+
assert m === Leaf[2]
|
463
|
+
m.assigns.must_equal [nil, Leaf[2], 2]
|
464
|
+
end
|
465
|
+
it do
|
466
|
+
m = ~Leaf.(->(v) { v > 1 }) & Leaf.(~any)
|
467
|
+
assert m === Leaf[2]
|
468
|
+
m.assigns.must_equal [Leaf[2], 2]
|
469
|
+
end
|
470
|
+
|
471
|
+
end
|
472
|
+
|
473
|
+
describe 'equality' do
|
474
|
+
data = (0..1).map do
|
475
|
+
[Empty,
|
476
|
+
Leaf[1],
|
477
|
+
Node[Empty, Leaf[1]],
|
478
|
+
Node[Node[Empty, Leaf[1]], Leaf[1]]]
|
479
|
+
end
|
480
|
+
data[0].zip(data[1]).each do |tree1, tree2|
|
481
|
+
it "equals #{tree1}" do
|
482
|
+
refute tree1.object_id == tree2.object_id, [tree1.object_id, tree2.object_id] unless tree1 == Empty
|
483
|
+
assert tree1 == tree2
|
484
|
+
end
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
488
|
+
describe 'list' do
|
489
|
+
it { List.(any, any) === List[1, Empty] }
|
490
|
+
it { List.(any, List) === List[1, Empty] }
|
491
|
+
end
|
492
|
+
|
493
|
+
describe 'binary tree' do
|
494
|
+
type_def { b_tree === tip | b_node(Object, b_tree, b_tree) }
|
495
|
+
end
|
496
|
+
|
497
|
+
end
|
498
|
+
|
499
|
+
|
500
|
+
#require 'benchmark'
|
501
|
+
#
|
502
|
+
#include Algebrick
|
503
|
+
#
|
504
|
+
#class None < Atom
|
505
|
+
#end
|
506
|
+
#
|
507
|
+
#class Some < Product
|
508
|
+
# fields Object
|
509
|
+
#end
|
510
|
+
#
|
511
|
+
#Maybe = Variant.new do
|
512
|
+
# variants None, Some
|
513
|
+
#end
|
514
|
+
#count = 1000_000
|
515
|
+
#
|
516
|
+
#Benchmark.bmbm(10) do |b|
|
517
|
+
# b.report('nil') do
|
518
|
+
# count.times do
|
519
|
+
# v = [Object.new, nil].sample
|
520
|
+
# case v
|
521
|
+
# when Object
|
522
|
+
# true
|
523
|
+
# when nil
|
524
|
+
# false
|
525
|
+
# end
|
526
|
+
# end
|
527
|
+
# end
|
528
|
+
# b.report('Maybe') do
|
529
|
+
# count.times do
|
530
|
+
# v = [Some[Object.new], None].sample
|
531
|
+
# case v
|
532
|
+
# when Some
|
533
|
+
# true
|
534
|
+
# when Maybe
|
535
|
+
# false
|
536
|
+
# end
|
537
|
+
# end
|
538
|
+
# end
|
539
|
+
#
|
540
|
+
#
|
541
|
+
#end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: algebrick
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Petr Chalupa
|
@@ -104,9 +104,11 @@ extra_rdoc_files:
|
|
104
104
|
- README_FULL.md
|
105
105
|
files:
|
106
106
|
- lib/algebrick.rb
|
107
|
+
- VERSION
|
107
108
|
- MIT-LICENSE
|
108
109
|
- README.md
|
109
110
|
- README_FULL.md
|
111
|
+
- spec/algebrick.rb
|
110
112
|
homepage: https://github.com/pitr-ch/algebrick
|
111
113
|
licenses:
|
112
114
|
- MIT
|
@@ -131,5 +133,6 @@ rubygems_version: 2.0.0
|
|
131
133
|
signing_key:
|
132
134
|
specification_version: 4
|
133
135
|
summary: Algebraic types and pattern matching for Ruby
|
134
|
-
test_files:
|
136
|
+
test_files:
|
137
|
+
- spec/algebrick.rb
|
135
138
|
has_rdoc:
|