Skip to content

Commit 21c577b

Browse files
authored
Improve interface type compaisons (#308)
When compared against a module, `_Interface` will inspect the instance methods defined on that module.
1 parent fb28595 commit 21c577b

File tree

2 files changed

+25
-12
lines changed

2 files changed

+25
-12
lines changed

lib/literal/types/interface_type.rb

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,14 @@
22

33
# @api private
44
class Literal::Types::InterfaceType
5-
# TODO: We can generate this and make it much more extensive.
6-
METHOD_TYPE_MAPPINGS = {
7-
:call => Set[Proc, Method],
8-
:to_proc => Set[Proc, Method, Symbol],
9-
:to_s => Set[String],
10-
}.freeze
11-
125
include Literal::Type
136

7+
# List of `===` method owners where the comparison will only match for objects with the same class
8+
OwnClassTypeMethodOwners = Set[String, Integer, Kernel, Float, NilClass, TrueClass, FalseClass].freeze
9+
1410
def initialize(*methods)
1511
raise Literal::ArgumentError.new("_Interface type must have at least one method.") if methods.size < 1
16-
@methods = methods
12+
@methods = methods.to_set.freeze
1713
freeze
1814
end
1915

@@ -24,21 +20,30 @@ def inspect
2420
end
2521

2622
def ===(value)
27-
@methods.all? { |m| value.respond_to?(m) }
23+
@methods.each do |method|
24+
return false unless value.respond_to?(method)
25+
end
26+
27+
true
2828
end
2929

3030
def >=(other)
3131
case other
3232
when Literal::Types::InterfaceType
33-
@methods.all? { |m| other.methods.include?(m) }
33+
@methods.subset?(other.methods)
3434
when Module
35-
@methods.map { |m| METHOD_TYPE_MAPPINGS[m] }.all? { |types| types&.include?(other) }
35+
public_methods = other.public_instance_methods.to_set
36+
@methods.subset?(public_methods)
3637
when Literal::Types::IntersectionType
3738
other.types.any? { |type| Literal.subtype?(type, self) }
3839
when Literal::Types::ConstraintType
3940
other.object_constraints.any? { |type| Literal.subtype?(type, self) }
4041
else
41-
false
42+
if OwnClassTypeMethodOwners.include?(other.method(:===).owner)
43+
self === other
44+
else
45+
false
46+
end
4247
end
4348
end
4449

test/types/_interface.test.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@
2323
refute_subtype _Interface(:b), _Interface(:a)
2424
refute_subtype _Interface(:a), _Interface(:a, :b)
2525
refute_subtype Proc, _Interface(:to_proc, :random_method)
26+
27+
assert_subtype "Hello", _Interface(:to_s)
28+
assert_subtype 1, _Interface(:+, :-)
29+
assert_subtype Object.new, _Interface(:inspect)
30+
assert_subtype 1.234, _Interface(:to_i)
31+
assert_subtype nil, _Interface(:nil?)
32+
assert_subtype true, _Interface(:to_s)
33+
assert_subtype false, _Interface(:to_s)
2634
end
2735

2836
test "error message" do

0 commit comments

Comments
 (0)