algebrick 0.1.1 → 0.1.2
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 +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:
|