algebrick 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +191 -0
- data/README.md +50 -2
- data/README_FULL.md +5 -5
- data/VERSION +1 -1
- data/lib/algebrick.rb +64 -41
- metadata +34 -7
- data/MIT-LICENSE +0 -19
- data/spec/algebrick.rb +0 -541
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb30a6eeabbe7769c1f946395bef047d5acf4362
|
4
|
+
data.tar.gz: 5c0289625f919dffe3f57e6fa2e258ca353d4ba9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0932e96464a044f3e2a9138efcd07433bf17fb747f1718b73354599750b84543e70f7b29a97db0854343f6f25c04eda02bc6baa8498a90e5feb55f2ac57437ad
|
7
|
+
data.tar.gz: 7bf6898feafadd9694cf898b61c81c9879fcc0dc628044244eed4f9ec5c8d1b3d2888b826305a73d463e08621f79e6759a75b5c84283a4bb48c617d27469671f
|
data/LICENSE
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
Apache License
|
2
|
+
Version 2.0, January 2004
|
3
|
+
http://www.apache.org/licenses/
|
4
|
+
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
6
|
+
|
7
|
+
1. Definitions.
|
8
|
+
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction, and
|
10
|
+
distribution as defined by Sections 1 through 9 of this document.
|
11
|
+
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
13
|
+
owner that is granting the License.
|
14
|
+
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all other entities
|
16
|
+
that control, are controlled by, or are under common control with that entity.
|
17
|
+
For the purposes of this definition, "control" means (i) the power, direct or
|
18
|
+
indirect, to cause the direction or management of such entity, whether by
|
19
|
+
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
20
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
21
|
+
|
22
|
+
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
23
|
+
permissions granted by this License.
|
24
|
+
|
25
|
+
"Source" form shall mean the preferred form for making modifications, including
|
26
|
+
but not limited to software source code, documentation source, and configuration
|
27
|
+
files.
|
28
|
+
|
29
|
+
"Object" form shall mean any form resulting from mechanical transformation or
|
30
|
+
translation of a Source form, including but not limited to compiled object code,
|
31
|
+
generated documentation, and conversions to other media types.
|
32
|
+
|
33
|
+
"Work" shall mean the work of authorship, whether in Source or Object form, made
|
34
|
+
available under the License, as indicated by a copyright notice that is included
|
35
|
+
in or attached to the work (an example is provided in the Appendix below).
|
36
|
+
|
37
|
+
"Derivative Works" shall mean any work, whether in Source or Object form, that
|
38
|
+
is based on (or derived from) the Work and for which the editorial revisions,
|
39
|
+
annotations, elaborations, or other modifications represent, as a whole, an
|
40
|
+
original work of authorship. For the purposes of this License, Derivative Works
|
41
|
+
shall not include works that remain separable from, or merely link (or bind by
|
42
|
+
name) to the interfaces of, the Work and Derivative Works thereof.
|
43
|
+
|
44
|
+
"Contribution" shall mean any work of authorship, including the original version
|
45
|
+
of the Work and any modifications or additions to that Work or Derivative Works
|
46
|
+
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
47
|
+
by the copyright owner or by an individual or Legal Entity authorized to submit
|
48
|
+
on behalf of the copyright owner. For the purposes of this definition,
|
49
|
+
"submitted" means any form of electronic, verbal, or written communication sent
|
50
|
+
to the Licensor or its representatives, including but not limited to
|
51
|
+
communication on electronic mailing lists, source code control systems, and
|
52
|
+
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
53
|
+
the purpose of discussing and improving the Work, but excluding communication
|
54
|
+
that is conspicuously marked or otherwise designated in writing by the copyright
|
55
|
+
owner as "Not a Contribution."
|
56
|
+
|
57
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
58
|
+
of whom a Contribution has been received by Licensor and subsequently
|
59
|
+
incorporated within the Work.
|
60
|
+
|
61
|
+
2. Grant of Copyright License.
|
62
|
+
|
63
|
+
Subject to the terms and conditions of this License, each Contributor hereby
|
64
|
+
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
65
|
+
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
66
|
+
publicly display, publicly perform, sublicense, and distribute the Work and such
|
67
|
+
Derivative Works in Source or Object form.
|
68
|
+
|
69
|
+
3. Grant of Patent License.
|
70
|
+
|
71
|
+
Subject to the terms and conditions of this License, each Contributor hereby
|
72
|
+
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
73
|
+
irrevocable (except as stated in this section) patent license to make, have
|
74
|
+
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
75
|
+
such license applies only to those patent claims licensable by such Contributor
|
76
|
+
that are necessarily infringed by their Contribution(s) alone or by combination
|
77
|
+
of their Contribution(s) with the Work to which such Contribution(s) was
|
78
|
+
submitted. If You institute patent litigation against any entity (including a
|
79
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
80
|
+
Contribution incorporated within the Work constitutes direct or contributory
|
81
|
+
patent infringement, then any patent licenses granted to You under this License
|
82
|
+
for that Work shall terminate as of the date such litigation is filed.
|
83
|
+
|
84
|
+
4. Redistribution.
|
85
|
+
|
86
|
+
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
87
|
+
in any medium, with or without modifications, and in Source or Object form,
|
88
|
+
provided that You meet the following conditions:
|
89
|
+
|
90
|
+
You must give any other recipients of the Work or Derivative Works a copy of
|
91
|
+
this License; and
|
92
|
+
You must cause any modified files to carry prominent notices stating that You
|
93
|
+
changed the files; and
|
94
|
+
You must retain, in the Source form of any Derivative Works that You distribute,
|
95
|
+
all copyright, patent, trademark, and attribution notices from the Source form
|
96
|
+
of the Work, excluding those notices that do not pertain to any part of the
|
97
|
+
Derivative Works; and
|
98
|
+
If the Work includes a "NOTICE" text file as part of its distribution, then any
|
99
|
+
Derivative Works that You distribute must include a readable copy of the
|
100
|
+
attribution notices contained within such NOTICE file, excluding those notices
|
101
|
+
that do not pertain to any part of the Derivative Works, in at least one of the
|
102
|
+
following places: within a NOTICE text file distributed as part of the
|
103
|
+
Derivative Works; within the Source form or documentation, if provided along
|
104
|
+
with the Derivative Works; or, within a display generated by the Derivative
|
105
|
+
Works, if and wherever such third-party notices normally appear. The contents of
|
106
|
+
the NOTICE file are for informational purposes only and do not modify the
|
107
|
+
License. You may add Your own attribution notices within Derivative Works that
|
108
|
+
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
109
|
+
provided that such additional attribution notices cannot be construed as
|
110
|
+
modifying the License.
|
111
|
+
You may add Your own copyright statement to Your modifications and may provide
|
112
|
+
additional or different license terms and conditions for use, reproduction, or
|
113
|
+
distribution of Your modifications, or for any such Derivative Works as a whole,
|
114
|
+
provided Your use, reproduction, and distribution of the Work otherwise complies
|
115
|
+
with the conditions stated in this License.
|
116
|
+
|
117
|
+
5. Submission of Contributions.
|
118
|
+
|
119
|
+
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
120
|
+
for inclusion in the Work by You to the Licensor shall be under the terms and
|
121
|
+
conditions of this License, without any additional terms or conditions.
|
122
|
+
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
123
|
+
any separate license agreement you may have executed with Licensor regarding
|
124
|
+
such Contributions.
|
125
|
+
|
126
|
+
6. Trademarks.
|
127
|
+
|
128
|
+
This License does not grant permission to use the trade names, trademarks,
|
129
|
+
service marks, or product names of the Licensor, except as required for
|
130
|
+
reasonable and customary use in describing the origin of the Work and
|
131
|
+
reproducing the content of the NOTICE file.
|
132
|
+
|
133
|
+
7. Disclaimer of Warranty.
|
134
|
+
|
135
|
+
Unless required by applicable law or agreed to in writing, Licensor provides the
|
136
|
+
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
|
137
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
138
|
+
including, without limitation, any warranties or conditions of TITLE,
|
139
|
+
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
140
|
+
solely responsible for determining the appropriateness of using or
|
141
|
+
redistributing the Work and assume any risks associated with Your exercise of
|
142
|
+
permissions under this License.
|
143
|
+
|
144
|
+
8. Limitation of Liability.
|
145
|
+
|
146
|
+
In no event and under no legal theory, whether in tort (including negligence),
|
147
|
+
contract, or otherwise, unless required by applicable law (such as deliberate
|
148
|
+
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
149
|
+
liable to You for damages, including any direct, indirect, special, incidental,
|
150
|
+
or consequential damages of any character arising as a result of this License or
|
151
|
+
out of the use or inability to use the Work (including but not limited to
|
152
|
+
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
153
|
+
any and all other commercial damages or losses), even if such Contributor has
|
154
|
+
been advised of the possibility of such damages.
|
155
|
+
|
156
|
+
9. Accepting Warranty or Additional Liability.
|
157
|
+
|
158
|
+
While redistributing the Work or Derivative Works thereof, You may choose to
|
159
|
+
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
160
|
+
other liability obligations and/or rights consistent with this License. However,
|
161
|
+
in accepting such obligations, You may act only on Your own behalf and on Your
|
162
|
+
sole responsibility, not on behalf of any other Contributor, and only if You
|
163
|
+
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
164
|
+
incurred by, or claims asserted against, such Contributor by reason of your
|
165
|
+
accepting any such warranty or additional liability.
|
166
|
+
|
167
|
+
END OF TERMS AND CONDITIONS
|
168
|
+
|
169
|
+
APPENDIX: How to apply the Apache License to your work
|
170
|
+
|
171
|
+
To apply the Apache License to your work, attach the following boilerplate
|
172
|
+
notice, with the fields enclosed by brackets "[]" replaced with your own
|
173
|
+
identifying information. (Don't include the brackets!) The text should be
|
174
|
+
enclosed in the appropriate comment syntax for the file format. We also
|
175
|
+
recommend that a file or class name and description of purpose be included on
|
176
|
+
the same "printed page" as the copyright notice for easier identification within
|
177
|
+
third-party archives.
|
178
|
+
|
179
|
+
# Copyright 2013 Petr Chalupa <git@pitr.ch>
|
180
|
+
#
|
181
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
182
|
+
# you may not use this file except in compliance with the License.
|
183
|
+
# You may obtain a copy of the License at
|
184
|
+
#
|
185
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
186
|
+
#
|
187
|
+
# Unless required by applicable law or agreed to in writing, software
|
188
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
189
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
190
|
+
# See the License for the specific language governing permissions and
|
191
|
+
# limitations under the License.
|
data/README.md
CHANGED
@@ -1,7 +1,55 @@
|
|
1
|
-
|
1
|
+
# Algebrick
|
2
2
|
|
3
|
-
|
3
|
+
[![Build Status](https://travis-ci.org/pitr-ch/algebrick.png?branch=master)](https://travis-ci.org/pitr-ch/algebrick)
|
4
|
+
|
5
|
+
It's a small gem providing **algebraic types** and **pattern matching** on them for Ruby.
|
4
6
|
|
5
7
|
- Documentation: <http://blog.pitr.ch/algebrick>
|
6
8
|
- Source: <https://github.com/pitr-ch/algebrick>
|
7
9
|
- Blog: <http://blog.pitr.ch/blog/categories/algebrick/>
|
10
|
+
|
11
|
+
## What is it good for?
|
12
|
+
|
13
|
+
- Defining data structures.
|
14
|
+
- Algebraic types play nice with JSON serialization and deserialization. It is ideal for defining
|
15
|
+
message-based cross-process communication.
|
16
|
+
- and more...
|
17
|
+
|
18
|
+
## Quick example
|
19
|
+
|
20
|
+
Load DSL for type definition and define some algebraic types
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
extend Algebrick::DSL
|
24
|
+
|
25
|
+
type_def do
|
26
|
+
tree === empty | leaf(Integer) | node(tree, tree)
|
27
|
+
end
|
28
|
+
```
|
29
|
+
|
30
|
+
Now types `Tree(Empty | Leaf | Node)`, `Empty`, `Leaf(Integer)` and `Node(Tree, Tree)` are defined.
|
31
|
+
Lets add some methods, don't miss the **pattern matching** example.
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
module Tree
|
35
|
+
# compute depth of a tree
|
36
|
+
def depth
|
37
|
+
match self,
|
38
|
+
Empty >> 0,
|
39
|
+
Leaf >> 1,
|
40
|
+
# ~ will store and pass matched parts to variables left and right
|
41
|
+
Node.(~any, ~any) --> left, right do
|
42
|
+
1 + [left.depth, right.depth].max
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
Methods are defined on **all** values of type Tree
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
Empty.depth # => 0
|
52
|
+
Leaf[10].depth # => 1
|
53
|
+
Node[Leaf[4], Empty].depth # => 2
|
54
|
+
Node[Empty, Node[Leaf[1], Empty]].depth # => 3
|
55
|
+
```
|
data/README_FULL.md
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# Algebrick
|
2
2
|
|
3
|
-
Is a small gem providing algebraic types and pattern matching on them for Ruby.
|
3
|
+
Is a small gem providing **algebraic types** and **pattern matching** on them for Ruby.
|
4
4
|
|
5
5
|
- Documentation: <http://blog.pitr.ch/algebrick>
|
6
6
|
- Source: <https://github.com/pitr-ch/algebrick>
|
7
7
|
- Blog: <http://blog.pitr.ch/blog/categories/algebrick/>
|
8
8
|
|
9
|
-
## Quick example
|
9
|
+
## Quick example
|
10
10
|
|
11
|
-
{include:file:doc/
|
11
|
+
{include:file:doc/quick_example.out.rb}
|
12
12
|
|
13
13
|
## Algebraic types: what is it?
|
14
14
|
|
@@ -62,8 +62,8 @@ Algebraic matchers are helper objects to match algebraic objects and others with
|
|
62
62
|
|
63
63
|
### Serialization
|
64
64
|
|
65
|
-
Algebraic types also play nice with JSON serialization.
|
66
|
-
|
65
|
+
Algebraic types also play nice with JSON serialization and deserialization. It is ideal for defining
|
66
|
+
messege-based cross-process comunication.
|
67
67
|
|
68
68
|
{include:file:doc/json.out.rb}
|
69
69
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.3
|
data/lib/algebrick.rb
CHANGED
@@ -1,4 +1,20 @@
|
|
1
|
-
#
|
1
|
+
# Copyright 2013 Petr Chalupa <git@pitr.ch>
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
|
16
|
+
# TODO method definition in variant type defines methods on variants based on match, better performance?
|
17
|
+
# TODO type variables/constructor maybe(a) === none | a
|
2
18
|
|
3
19
|
require 'set'
|
4
20
|
|
@@ -18,9 +34,9 @@ module Algebrick
|
|
18
34
|
end
|
19
35
|
|
20
36
|
module TypeCheck
|
21
|
-
|
22
|
-
|
23
|
-
|
37
|
+
def is_kind_of?(value, *types)
|
38
|
+
a_type_check :kind_of?, false, value, *types
|
39
|
+
end
|
24
40
|
|
25
41
|
def is_kind_of!(value, *types)
|
26
42
|
a_type_check :kind_of?, true, value, *types
|
@@ -191,6 +207,9 @@ module Algebrick
|
|
191
207
|
end
|
192
208
|
end
|
193
209
|
|
210
|
+
TYPE_KEY = :algebrick
|
211
|
+
FIELDS_KEY = :fields
|
212
|
+
|
194
213
|
class Atom < Type
|
195
214
|
include Value
|
196
215
|
|
@@ -220,7 +239,7 @@ module Algebrick
|
|
220
239
|
end
|
221
240
|
|
222
241
|
def to_hash
|
223
|
-
{
|
242
|
+
{ TYPE_KEY => name }
|
224
243
|
end
|
225
244
|
|
226
245
|
def from_hash(hash)
|
@@ -263,12 +282,12 @@ module Algebrick
|
|
263
282
|
end
|
264
283
|
|
265
284
|
def to_hash
|
266
|
-
{ self.class.type.name
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
285
|
+
{ TYPE_KEY => self.class.type.name }.
|
286
|
+
update(if type.field_names
|
287
|
+
type.field_names.inject({}) { |h, name| h.update name => hashize(self[name]) }
|
288
|
+
else
|
289
|
+
{ FIELDS_KEY => fields.map { |v| hashize v } }
|
290
|
+
end)
|
272
291
|
end
|
273
292
|
|
274
293
|
def ==(other)
|
@@ -346,21 +365,13 @@ module Algebrick
|
|
346
365
|
end
|
347
366
|
|
348
367
|
def set_fields(fields_or_hash)
|
349
|
-
fields = if fields_or_hash.size == 1 && fields_or_hash.first.is_a?(Hash)
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
if keys
|
357
|
-
@field_names = keys
|
358
|
-
keys.all? { |k| is_kind_of! k, Symbol }
|
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 }
|
362
|
-
define_method(:[]) { |key| @fields[dict[key]] }
|
363
|
-
end
|
368
|
+
fields, keys = if fields_or_hash.size == 1 && fields_or_hash.first.is_a?(Hash)
|
369
|
+
[fields_or_hash.first.values, fields_or_hash.first.keys]
|
370
|
+
else
|
371
|
+
[fields_or_hash, nil]
|
372
|
+
end
|
373
|
+
|
374
|
+
set_field_names keys if keys
|
364
375
|
|
365
376
|
fields.all? { |f| is_kind_of! f, Type, Class }
|
366
377
|
raise TypeError, 'there is no product with zero fields' unless fields.size > 0
|
@@ -369,6 +380,15 @@ module Algebrick
|
|
369
380
|
@constructor = Class.new(ProductConstructor).tap { |c| c.type = self }
|
370
381
|
end
|
371
382
|
|
383
|
+
def set_field_names(names)
|
384
|
+
@field_names = names
|
385
|
+
names.all? { |k| is_kind_of! k, Symbol }
|
386
|
+
dict = @field_indexes =
|
387
|
+
Hash.new { |h, k| raise ArgumentError, "uknown field #{k.inspect}" }.
|
388
|
+
update names.each_with_index.inject({}) { |h, (k, i)| h.update k => i }
|
389
|
+
define_method(:[]) { |key| @fields[dict[key]] }
|
390
|
+
end
|
391
|
+
|
372
392
|
def set_variants(variants)
|
373
393
|
variants.all? { |v| is_kind_of! v, Type, Class }
|
374
394
|
@variants = variants
|
@@ -399,10 +419,14 @@ module Algebrick
|
|
399
419
|
end
|
400
420
|
|
401
421
|
def product_from_hash(hash)
|
402
|
-
|
403
|
-
|
422
|
+
(type_name = hash[TYPE_KEY] || hash[TYPE_KEY.to_s]) or
|
423
|
+
raise ArgumentError, "hash does not have #{TYPE_KEY}"
|
404
424
|
raise ArgumentError, "#{type_name} is not #{name}" unless type_name == name
|
425
|
+
|
426
|
+
fields = hash[FIELDS_KEY] || hash[FIELDS_KEY.to_s] ||
|
427
|
+
hash.reject { |k, _| k.to_s == TYPE_KEY.to_s }
|
405
428
|
is_kind_of! fields, Hash, Array
|
429
|
+
|
406
430
|
case fields
|
407
431
|
when Array
|
408
432
|
self[*fields.map { |value| field_from_hash value }]
|
@@ -416,14 +440,9 @@ module Algebrick
|
|
416
440
|
|
417
441
|
def field_from_hash(hash)
|
418
442
|
return hash unless Hash === hash
|
419
|
-
|
420
|
-
|
421
|
-
type
|
422
|
-
if type.respond_to? :from_hash
|
423
|
-
type.from_hash hash
|
424
|
-
else
|
425
|
-
value
|
426
|
-
end
|
443
|
+
(type_name = hash[TYPE_KEY] || hash[TYPE_KEY.to_s]) or return hash
|
444
|
+
type = constantize type_name
|
445
|
+
type.from_hash hash
|
427
446
|
end
|
428
447
|
|
429
448
|
def constantize(camel_cased_word)
|
@@ -446,10 +465,12 @@ module Algebrick
|
|
446
465
|
super(&block)
|
447
466
|
end
|
448
467
|
|
449
|
-
def
|
468
|
+
def new(*fields)
|
450
469
|
construct_product(*fields)
|
451
470
|
end
|
452
471
|
|
472
|
+
alias_method :[], :new
|
473
|
+
|
453
474
|
def be_kind_of!(type)
|
454
475
|
product_be_kind_of type
|
455
476
|
end
|
@@ -527,10 +548,12 @@ module Algebrick
|
|
527
548
|
Matchers::Variant.new self
|
528
549
|
end
|
529
550
|
|
530
|
-
def
|
551
|
+
def new(*fields)
|
531
552
|
construct_product(*fields)
|
532
553
|
end
|
533
554
|
|
555
|
+
alias_method :[], :new
|
556
|
+
|
534
557
|
def ==(other)
|
535
558
|
other.kind_of? ProductVariant and
|
536
559
|
variants == other.variants and fields == other.fields
|
@@ -951,7 +974,7 @@ module Algebrick
|
|
951
974
|
Object
|
952
975
|
else
|
953
976
|
base
|
954
|
-
|
977
|
+
end
|
955
978
|
@pre_types = {}
|
956
979
|
instance_eval &definition
|
957
980
|
end
|
@@ -1010,8 +1033,8 @@ module Algebrick
|
|
1010
1033
|
|
1011
1034
|
select.(ProductVariant) do |name, type, pre_type|
|
1012
1035
|
type.send :initialize,
|
1013
|
-
|
1014
|
-
|
1036
|
+
pre_type.fields.map { |f| get_class f },
|
1037
|
+
pre_type.variants.map { |v| get_class v }
|
1015
1038
|
end
|
1016
1039
|
end
|
1017
1040
|
|
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.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Petr Chalupa
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest-reporters
|
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'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: turn
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -67,7 +81,21 @@ dependencies:
|
|
67
81
|
- !ruby/object:Gem::Version
|
68
82
|
version: '0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
84
|
+
name: redcarpet
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: github-markup
|
71
99
|
requirement: !ruby/object:Gem::Requirement
|
72
100
|
requirements:
|
73
101
|
- - '>='
|
@@ -99,16 +127,16 @@ email: git@pitr.ch
|
|
99
127
|
executables: []
|
100
128
|
extensions: []
|
101
129
|
extra_rdoc_files:
|
102
|
-
-
|
130
|
+
- LICENSE
|
103
131
|
- README.md
|
104
132
|
- README_FULL.md
|
133
|
+
- VERSION
|
105
134
|
files:
|
106
135
|
- lib/algebrick.rb
|
107
136
|
- VERSION
|
108
|
-
-
|
137
|
+
- LICENSE
|
109
138
|
- README.md
|
110
139
|
- README_FULL.md
|
111
|
-
- spec/algebrick.rb
|
112
140
|
homepage: https://github.com/pitr-ch/algebrick
|
113
141
|
licenses:
|
114
142
|
- MIT
|
@@ -133,6 +161,5 @@ rubygems_version: 2.0.0
|
|
133
161
|
signing_key:
|
134
162
|
specification_version: 4
|
135
163
|
summary: Algebraic types and pattern matching for Ruby
|
136
|
-
test_files:
|
137
|
-
- spec/algebrick.rb
|
164
|
+
test_files: []
|
138
165
|
has_rdoc:
|
data/MIT-LICENSE
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
Copyright (c) 2013 Petr Chalupa <git@pitr.ch>
|
2
|
-
|
3
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
-
of this software and associated documentation files (the "Software"), to deal
|
5
|
-
in the Software without restriction, including without limitation the rights
|
6
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
-
copies of the Software, and to permit persons to whom the Software is
|
8
|
-
furnished to do so, subject to the following conditions:
|
9
|
-
|
10
|
-
The above copyright notice and this permission notice shall be included in
|
11
|
-
all copies or substantial portions of the Software.
|
12
|
-
|
13
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
-
THE SOFTWARE.
|
data/spec/algebrick.rb
DELETED
@@ -1,541 +0,0 @@
|
|
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
|