composite_type 0.1.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 79b73ddf6ca69a94ade50421d460271d88bfa71c
4
- data.tar.gz: eff100ef24de4b9136566c47bd123eafe63491d0
3
+ metadata.gz: ae4bbf53d86ecc2c5e9ff994ccb7d5314fa163bd
4
+ data.tar.gz: 8866e977bf37b1bb094b30bc9dfb101d7dd126f0
5
5
  SHA512:
6
- metadata.gz: 0be9100aa76f309188b51710feb30837f760c03556fa7a76f92541227811bb3499e62cbc8de7b395a045b47f17c98349968758c6800813773d4527263f805fe3
7
- data.tar.gz: d05c6b593fee921e608bcb581c3478fb1b2b1d85ba3f2dd5c7582dac0ff3d12d5ad976be1afd3f27657aee22e3054590725b73f6de2422a7bfc5da570fbb7d8e
6
+ metadata.gz: '06584bde3409c9835559d40125b4c3d63ebc53e40adcddf0aacf2db473ae524d87fc04e2617054991a06863cdaab5ffc86e86c6933b9eca6f049984fe5b6af73'
7
+ data.tar.gz: 92314b05a2ee942bd02781f30524f4d57bc5eed7d10b8f4d1cfec45dd6b7bce9a7b740ce01871b5ae1874b5a800a86561e34ad339768f2be20215e1cab34955d
data/README.md CHANGED
@@ -13,7 +13,7 @@ Defining types through Modules:
13
13
 
14
14
  module Even
15
15
  def self.=== x
16
- Integer === x and x.even?
16
+ Integer === x and x.even?
17
17
  end
18
18
  end
19
19
  Array.of(Even) === [ 2, 4, 10 ]
@@ -29,14 +29,17 @@ Thus composite types can be used in "case when" clauses:
29
29
  Logical operators: #|, #&, #~ are supported:
30
30
 
31
31
  a = [ 1, 2, 3 ]
32
- Array.of(Positive & Numeric) === a # => true
33
- Array.of(~ NilClass) === a # => false
32
+ Array.of(Positive & Integer) === a # => true
33
+ Array.of(~ NilClass) === a # => true
34
34
 
35
35
  b = [ 1, -2, 3 ]
36
- Array.of(Positive & Numeric) === b # => false
37
-
38
- c = [ 1, nil, 3 ]
39
- Array.of(~ NilClass) === c # => false
36
+ Array.of(Positive & Integer) === b # => false
37
+
38
+ c = [ 1, 1.5, 3 ]
39
+ Array.of(Positive & Integer) === c # => false
40
+
41
+ d = [ 1, nil, 3 ]
42
+ Array.of(~ NilClass) === d # => false
40
43
 
41
44
  Composite types are cached indefinitely, therefore anonymous Modules cannot be composed.
42
45
 
@@ -19,10 +19,8 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.3"
22
- spec.add_development_dependency "rake"
23
- spec.add_development_dependency "rspec", "~> 3.0"
24
22
  spec.add_development_dependency "simplecov", "~> 0.8"
25
- spec.add_development_dependency "pry", "~> 0.9"
26
- spec.add_development_dependency "guard", "~> 2.6"
27
- spec.add_development_dependency "guard-rspec", "~> 4.3"
23
+ spec.add_development_dependency "pry", "~> 0.10"
24
+ spec.add_development_dependency "guard", "~> 2.14"
25
+ spec.add_development_dependency "guard-rspec", "~> 4.7"
28
26
  end
@@ -23,16 +23,27 @@ class Module
23
23
  end
24
24
 
25
25
  # Matches nothing.
26
- module Void
26
+ module VoidType
27
27
  def === x
28
28
  false
29
29
  end
30
+
31
+ def >= t
32
+ self == t
33
+ end
30
34
  end
35
+ Void = Class.new.extend(VoidType)
31
36
 
32
37
  class ContainerType < CompositeType
33
38
  def === x
34
39
  @_t[0] === x and x.all?{|e| @_t[1] === e }
35
40
  end
41
+
42
+ def >= t
43
+ super or
44
+ t.is_a?(self.class) and @_t.zip(t._t).all?{|e1, e2| e1 >= e2 }
45
+ end
46
+
36
47
  def to_s
37
48
  @to_s ||= "#{@_t[0]}.of(#{@_t[1]})".freeze
38
49
  end
@@ -54,6 +65,12 @@ class Module
54
65
  @_t.all?{|t| t === x[i += 1]}
55
66
  end
56
67
  end
68
+
69
+ def >= t
70
+ super or
71
+ t.is_a?(self.class) and @_t.zip(t._t).all?{|e1, e2| e1 >= e2 }
72
+ end
73
+
57
74
  def to_s
58
75
  @to_s ||= "#{@_t[0]}.with(#{@_t[1..-1] * ','})".freeze
59
76
  end
@@ -61,8 +78,8 @@ class Module
61
78
 
62
79
  # Constructs a type of Enumerable elements.
63
80
  #
64
- # String.with(Integer, Float) === [ "foo", 1, 1.2 ]
65
- # Hash.of(String.with(Integer))
81
+ # String.with(Integer, Float) === [ "foo", 1, 1.2 ]
82
+ # Hash.of(String.with(Integer))
66
83
  def with *types
67
84
  EnumeratedType.new_cached(self, *types)
68
85
  end
@@ -71,6 +88,18 @@ class Module
71
88
  def === x
72
89
  @_t[0] === x or @_t[1] === x
73
90
  end
91
+
92
+ def >= t
93
+ case
94
+ when super
95
+ true
96
+ when t.is_a?(self.class)
97
+ t._t.all?{|e2| @_t.any?{|e1| e1 >= e2}}
98
+ else
99
+ @_t.any?{|e1| e1 >= t}
100
+ end
101
+ end
102
+
74
103
  def to_s
75
104
  @to_s ||= "(#{@_t[0]}|#{@_t[1]})".freeze
76
105
  end
@@ -78,7 +107,7 @@ class Module
78
107
 
79
108
  # Constructs a type which can be A OR B.
80
109
  #
81
- # Array.of(String|Integer)
110
+ # Array.of(String|Integer)
82
111
  def | t
83
112
  case
84
113
  when t <= self
@@ -86,7 +115,9 @@ class Module
86
115
  when self <= t
87
116
  t
88
117
  else
89
- DisjunctiveType.new_cached(self, t)
118
+ a, b = self, t
119
+ a, b = b, a if a.to_s > b.to_s
120
+ DisjunctiveType.new_cached(a, b)
90
121
  end
91
122
  end
92
123
 
@@ -94,6 +125,18 @@ class Module
94
125
  def === x
95
126
  @_t[0] === x and @_t[1] === x
96
127
  end
128
+
129
+ def >= t
130
+ case
131
+ when super
132
+ true
133
+ when t.is_a?(self.class)
134
+ t._t.all?{|e2| @_t.all?{|e1| e1 >= e2}}
135
+ else
136
+ @_t.all?{|e1| e1 >= t}
137
+ end
138
+ end
139
+
97
140
  def to_s
98
141
  @to_s ||= "(#{@_t[0]}&#{@_t[1]})".freeze
99
142
  end
@@ -103,24 +146,47 @@ class Module
103
146
  #
104
147
  # Array.of(Positive & Integer)
105
148
  def & t
106
- ConjunctiveType.new_cached(self, t)
149
+ case
150
+ when equal?(t)
151
+ self
152
+ else
153
+ a, b = self, t
154
+ a, b = b, a if a.to_s > b.to_s
155
+ ConjunctiveType.new_cached(a, b)
156
+ end
107
157
  end
108
158
 
109
159
  class NegativeType < CompositeType
110
160
  def === x
111
161
  ! (@_t[0] === x)
112
162
  end
163
+
164
+ def >= t
165
+ t.is_a?(self.class) and
166
+ t._t[0] >= @_t[0]
167
+ end
168
+
113
169
  def to_s
114
170
  @to_s ||= "(~#{@_t[0]})".freeze
115
171
  end
172
+
173
+ INVERSE_MAP = {
174
+ }
175
+ def self.inverse(a, b)
176
+ INVERSE_MAP[a] = b
177
+ INVERSE_MAP[b] = a
178
+ end
179
+ inverse(Object, Void)
116
180
  end
117
181
 
118
182
  # Constructs a type which must not be A.
119
183
  #
120
184
  # Array.of(~ NilClass)
121
185
  def ~@
122
- case self
123
- when NegativeType
186
+ case
187
+ when x = NegativeType::INVERSE_MAP[self]
188
+ x
189
+ when self.is_a?(NegativeType)
124
190
  self._t.first
125
191
  else
126
192
  NegativeType.new_cached(self)
@@ -161,6 +227,7 @@ module NonPositive
161
227
  def self.=== x
162
228
  x <= 0 rescue nil
163
229
  end
230
+ Module::NegativeType.inverse(self, Positive)
164
231
  end
165
232
 
166
233
  # Objects that are Numericlike and >= 0.
@@ -168,6 +235,7 @@ module NonNegative
168
235
  def self.=== x
169
236
  x >= 0 rescue nil
170
237
  end
238
+ Module::NegativeType.inverse(self, Negative)
171
239
  end
172
240
 
173
241
  # Objects that can do IO.
@@ -1,3 +1,3 @@
1
1
  module CompositeType
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.2"
3
3
  end
@@ -1,7 +1,14 @@
1
1
  require 'spec_helper'
2
2
  require 'composite_type'
3
3
 
4
- describe Class::CompositeType do
4
+ describe Module::CompositeType do
5
+ module A; end
6
+ module B; end
7
+ module C; include A; end
8
+ module D; include B; end
9
+ module E; include A, B; end
10
+ module F; include A, B; end
11
+
5
12
  it "should cache instances" do
6
13
  t1 = Array.of(String.with(~ Float | Symbol & Hash))
7
14
  t2 = Array.of(String.with(~ Float | Symbol & Hash))
@@ -14,6 +21,20 @@ describe Class::CompositeType do
14
21
  }.to raise_error(Module::CompositeType::Error, "cannot create CompositeType from unamed object")
15
22
  end
16
23
 
24
+ context Module::Void do
25
+ subject { Module::Void }
26
+ it "does not match anything" do
27
+ expect(subject === Object.new) .to be_falsey
28
+ end
29
+ context "#>=" do
30
+ it "is disjoint from everything" do
31
+ expect( subject >= subject ) .to be_truthy
32
+ expect( Object >= subject ) .to be_truthy
33
+ expect( subject >= Object ) .to be_falsey
34
+ end
35
+ end
36
+ end
37
+
17
38
  context Numericlike do
18
39
  subject { Numericlike }
19
40
  let(:numeric_like) do
@@ -43,125 +64,102 @@ describe Class::CompositeType do
43
64
 
44
65
  context "EnumeratedType" do
45
66
  subject { Integer.with(Symbol, String) }
46
- it "should not fail" do
67
+ it "is true for respective unmatches" do
47
68
  expect(subject === [ 1, :symbol, "string" ]) .to be_truthy
48
69
  end
49
- it "should fail" do
70
+ it "is false for respective unmatches" do
50
71
  expect(subject === [ ]) .to be_falsey
51
- end
52
- it "should fail" do
53
72
  expect(subject === [ 1, :symbol, :wrong ]) .to be_falsey
54
73
  end
55
- it "should fail" do
74
+ it "is false for too many elements" do
56
75
  expect(subject === [ 1, :symbol, "string", :too_many]) .to be_falsey
57
76
  end
58
77
  end
59
78
 
60
79
  context "ContainerType" do
61
80
  subject { Array.of(String) }
62
- it "should not fail when empty" do
81
+ it "is true when empty" do
63
82
  expect(subject === [ ]) .to be_truthy
64
83
  end
65
- it "should not fail with a matching element" do
84
+ it "is true with a matching element" do
66
85
  expect(subject === [ "String" ]) .to be_truthy
67
86
  end
68
- it "should fail when contains unmatching element" do
87
+ it "is false with an unmatching element" do
69
88
  expect(subject === [ "String", 1234 ]) .to be_falsey
70
89
  end
71
- it "should fail when is not an Enumerable" do
90
+ it "is false without an Enumerable" do
72
91
  expect(subject === 1234) .to be_falsey
73
92
  end
74
- it "should fail when is not equvalent" do
93
+ it "is false when not equvalent" do
75
94
  expect(subject === { }) .to be_falsey
76
95
  end
77
96
  end
78
97
 
79
98
  context "EnumeratedType" do
80
99
  subject { Hash.of(String.with(Integer)) }
81
- it "should not fail when empty" do
100
+ it "matches" do
82
101
  expect(subject === { }) .to be_truthy
83
- end
84
-
85
- it "should not fail" do
86
102
  expect(subject === { "foo" => 1 }) .to be_truthy
87
- end
88
-
89
- it "should fail" do
90
103
  expect(subject === { "foo" => :symbol }) .to be_falsey
91
- end
92
-
93
- it "should fail" do
94
104
  expect(subject === { :symbol => 2 }) .to be_falsey
95
105
  end
96
106
  end
97
107
 
98
108
  context "DisjunctiveType" do
99
- it "should reduce to the greater type if A or B are subclasses of each other" do
100
- expect(Float | Numeric) .to equal(Numeric)
101
- expect(Numeric | Float ) .to equal(Numeric)
102
- expect(Numeric | Numeric) .to equal(Numeric)
103
- end
104
-
105
- it "should not reduce if A and B are disjunctive" do
106
- expect((Numericlike | Numeric).to_s) .to eq("(Numericlike|Numeric)")
107
- expect((String | Array).to_s) .to eq("(String|Array)")
109
+ it "matches" do
110
+ expect((String | Integer) === "String") .to be_truthy
111
+ expect((String | Integer) === 1234) .to be_truthy
112
+ expect((String | Integer) === :symbol) .to be_falsey
108
113
  end
109
114
 
110
- it "should not fail when empty" do
111
- v = [ ]
112
- expect(Array.of(String | Integer) === v) .to be_truthy
115
+ it "rewrites B | A => A | B" do
116
+ expect((B | A)) .to equal((A | B))
113
117
  end
114
118
 
115
- it "should not fail" do
116
- v = [ "String", 1234 ]
117
- expect(Array.of(String | Integer) === v) .to be_truthy
119
+ it "reduces to the greater type if A or B are subclasses of each other" do
120
+ expect(Float | Numeric) .to equal(Numeric)
121
+ expect(Numeric | Float ) .to equal(Numeric)
122
+ expect(Numeric | Numeric) .to equal(Numeric)
118
123
  end
119
124
 
120
- it "should fail" do
121
- v = [ "String", 1234, :symbol ]
122
- expect(Array.of(String | Integer) === v) .to be_falsey
125
+ it "does not reduce if A and B are disjunctive" do
126
+ expect((Numericlike | Numeric).to_s) .to eq("(Numeric|Numericlike)")
127
+ expect((String | Array).to_s) .to eq("(Array|String)")
123
128
  end
124
129
  end
125
130
 
126
131
  context "ConjunctiveType" do
127
- subject { Array.of(Positive & Integer) }
128
- it "should not fail when empty" do
129
- v = [ ]
130
- expect(subject === v) .to be_truthy
131
- end
132
-
133
- it "should not fail" do
134
- v = [ 1, 2 ]
135
- expect(subject === v) .to be_truthy
132
+ subject { Positive & Integer }
133
+ it "matches" do
134
+ expect(subject === 1) .to be_truthy
135
+ expect(subject === 0) .to be_falsey
136
+ expect(subject === 0.0) .to be_falsey
137
+ expect(subject === :symbol) .to be_falsey
136
138
  end
137
-
138
- it "should fail" do
139
- v = [ 0, 1 ]
140
- expect(subject === v) .to be_falsey
139
+ it "folds A & A => A" do
140
+ expect((A & A)) .to equal(A)
141
141
  end
142
-
143
- it "should fail" do
144
- v = [ 1, :symbol ]
145
- expect(subject === v) .to be_falsey
142
+ it "rewrites B & A => A & B" do
143
+ expect((B & A)) .to equal((A & B))
146
144
  end
147
145
  end
148
146
 
149
147
  context "NegativeType" do
150
- it "~~A == A" do
151
- t = String
152
- expect(~ ~ t) .to equal(t)
148
+ it "matches" do
149
+ expect((~ NilClass) === 1) .to be_truthy
150
+ expect((~ NilClass) === :symbol) .to be_truthy
151
+ expect((~ NilClass) === nil) .to be_falsey
153
152
  end
154
153
 
155
- it "should be true for match" do
156
- v = [ 1, :symbol ]
157
- expect(Array.of(~ NilClass) === v) .to be_truthy
154
+ it "rewrites (~ (~ A)) => A" do
155
+ expect(~ ~ A) .to equal(A)
158
156
  end
159
157
 
160
- it "should be false for match" do
161
- v = [ 1, nil, :symbol ]
162
- expect((~ NilClass) === 1) .to be_truthy
163
- expect((~ NilClass) === nil) .to be_falsey
164
- expect(Array.of(~ NilClass) === v) .to be_falsey
158
+ it "rewrites inverse types" do
159
+ expect(~ Object) .to equal(Module::Void)
160
+ expect(~ Module::Void) .to equal(Object)
161
+ expect(~ Negative) .to equal(NonNegative)
162
+ expect(~ NonNegative) .to equal(Negative)
165
163
  end
166
164
  end
167
165
 
@@ -209,6 +207,44 @@ describe Class::CompositeType do
209
207
  end
210
208
  end
211
209
 
210
+ context "#>=" do
211
+ it "returns true where left side is a supertype of right side" do
212
+ expect( Array.of(Object) >= Array.of(String) ) .to be_truthy
213
+ expect( Array.of(String) >= Array.of(Object) ) .to be_falsey
214
+ expect( Array.of(String) >= Object ) .to be_falsey
215
+
216
+ expect( Numeric.with(Integer) >= Numeric.with(Bignum) ) .to be_truthy
217
+ expect( Numeric.with(Integer) >= Float.with(Bignum) ) .to be_truthy
218
+ expect( Numeric.with(Integer) >= Object.with(Bignum) ) .to be_falsey
219
+ expect( Numeric.with(Integer) >= Float.with(Object) ) .to be_falsey
220
+ expect( Numeric.with(Integer) >= Integer.with(Object) ) .to be_falsey
221
+
222
+ expect( Hash.of(Symbol.with(Object)) >= Hash.of(Symbol.with(String)) ) .to be_truthy
223
+ expect( Hash.of(Symbol.with(Object)) >= Hash.of(Symbol.with(Fixnum)) ) .to be_truthy
224
+ expect( Hash.of(Symbol.with(String)) >= Hash.of(Symbol.with(Fixnum)) ) .to be_falsey
225
+ expect( Hash.of(Symbol.with(Object)) >= Array.of(String) ) .to be_falsey
226
+
227
+ expect( (Integer | Float) >= (Integer | Float) ) .to be_truthy
228
+ expect( (Integer | Float) >= Integer ) .to be_truthy
229
+ expect( (Integer | Float) >= Fixnum ) .to be_truthy
230
+ expect( (Integer | Float) >= Bignum ) .to be_truthy
231
+ expect( (Fixnum | Float) >= Bignum ) .to be_falsey
232
+ expect( (Fixnum | Float) >= (Float | Bignum) ) .to be_falsey
233
+ expect( (Integer | Float) >= (Float | Fixnum) ) .to be_truthy
234
+
235
+ expect( (Positive & Integer) >= (Integer & Positive) ) .to be_truthy
236
+ expect( (A & B) >= E ) .to be_truthy
237
+ expect( (A & B) >= C ) .to be_falsey
238
+ expect( (A & B) >= (E & F) ) .to be_truthy
239
+ expect( (Positive & Integer) >= Integer ) .to be_falsey
240
+
241
+ expect( (~ A) >= B ) .to be_falsey
242
+ expect( (~ Numeric) >= (~ Fixnum) ) .to be_falsey
243
+ expect( (~ Fixnum) >= (~ Numeric) ) .to be_truthy
244
+ expect( (~ Object) >= (~ Fixnum) ) .to be_falsey
245
+ end
246
+ end
247
+
212
248
  context "misc" do
213
249
  it "example 1" do
214
250
  h = { "a" => 1, "b" => :symbol }
@@ -223,6 +259,5 @@ describe Class::CompositeType do
223
259
  it "should handle to_s" do
224
260
  expect(Hash.of(String.with(Integer | Symbol)).to_s) .to eq("Hash.of(String.with((Integer|Symbol)))")
225
261
  end
226
-
227
262
  end
228
263
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: composite_type
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kurt Stephens
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-27 00:00:00.000000000 Z
11
+ date: 2016-12-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -24,34 +24,6 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.3'
27
- - !ruby/object:Gem::Dependency
28
- name: rake
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: rspec
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '3.0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '3.0'
55
27
  - !ruby/object:Gem::Dependency
56
28
  name: simplecov
57
29
  requirement: !ruby/object:Gem::Requirement
@@ -72,42 +44,42 @@ dependencies:
72
44
  requirements:
73
45
  - - "~>"
74
46
  - !ruby/object:Gem::Version
75
- version: '0.9'
47
+ version: '0.10'
76
48
  type: :development
77
49
  prerelease: false
78
50
  version_requirements: !ruby/object:Gem::Requirement
79
51
  requirements:
80
52
  - - "~>"
81
53
  - !ruby/object:Gem::Version
82
- version: '0.9'
54
+ version: '0.10'
83
55
  - !ruby/object:Gem::Dependency
84
56
  name: guard
85
57
  requirement: !ruby/object:Gem::Requirement
86
58
  requirements:
87
59
  - - "~>"
88
60
  - !ruby/object:Gem::Version
89
- version: '2.6'
61
+ version: '2.14'
90
62
  type: :development
91
63
  prerelease: false
92
64
  version_requirements: !ruby/object:Gem::Requirement
93
65
  requirements:
94
66
  - - "~>"
95
67
  - !ruby/object:Gem::Version
96
- version: '2.6'
68
+ version: '2.14'
97
69
  - !ruby/object:Gem::Dependency
98
70
  name: guard-rspec
99
71
  requirement: !ruby/object:Gem::Requirement
100
72
  requirements:
101
73
  - - "~>"
102
74
  - !ruby/object:Gem::Version
103
- version: '4.3'
75
+ version: '4.7'
104
76
  type: :development
105
77
  prerelease: false
106
78
  version_requirements: !ruby/object:Gem::Requirement
107
79
  requirements:
108
80
  - - "~>"
109
81
  - !ruby/object:Gem::Version
110
- version: '4.3'
82
+ version: '4.7'
111
83
  description: Composite Types for Ruby
112
84
  email:
113
85
  - ks.github@kurtstephens.com
@@ -147,7 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
147
119
  version: '0'
148
120
  requirements: []
149
121
  rubyforge_project:
150
- rubygems_version: 2.2.2
122
+ rubygems_version: 2.6.8
151
123
  signing_key:
152
124
  specification_version: 4
153
125
  summary: Array.of(String)