Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/literal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ def self.subtype?(type, supertype)
subtype = subtype.block.call if Types::DeferredType === subtype
supertype = supertype.block.call if Types::DeferredType === supertype

return true if subtype == Types::NeverType::Instance

return true if supertype == subtype

case supertype
Expand Down
4 changes: 4 additions & 0 deletions lib/literal/types.rb
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,10 @@ def _JSONData?(...)
)
end

def _Kind(type)
KindType.new(type)
end

# Matches if the value is a `Proc` and `#lambda?` returns truthy.
def _Lambda
LambdaType
Expand Down
2 changes: 1 addition & 1 deletion lib/literal/types/constraint_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def >=(other)
def <=(other)
case other
when Module
@object_constraints.any? { |constraint| Literal.subtype?(other, constraint) }
@object_constraints.any? { |constraint| Literal.subtype?(constraint, other) }
end
end

Expand Down
7 changes: 7 additions & 0 deletions lib/literal/types/intersection_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,12 @@ def >=(other)
end
end

def <=(other)
case other
when Module
@types.any? { |type| Literal.subtype?(type, other) }
end
end

freeze
end
18 changes: 18 additions & 0 deletions lib/literal/types/kind_type.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

class Literal::Types::KindType
include Literal::Type

def initialize(type)
@type = type
freeze
end

attr_reader :type

def ===(object)
Literal.subtype?(object, @type)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Accept wrapped container types in kind matching

KindType#=== currently relies on Literal.subtype?(object, @type), but module-based subtyping only succeeds for a small subset of literal wrapper types (ConstraintType, IntersectionType, UnionType, NeverType). As a result, _Kind(Array) rejects _Array(Integer) (and similarly for other wrappers like _Hash), even though those types are semantically constrained to arrays. This makes higher-order constraints unexpectedly fail for common generic type arguments.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No it doesn't. Look at the implementation of Literal.subtype?

end

freeze
end
6 changes: 5 additions & 1 deletion lib/literal/types/never_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@ def ===(value)

def >=(other)
case other
when Literal::Types::NeverTypeClass
when Literal::Types::NeverType
true
else
false
end
end

def <=(_other)
true
end

freeze
end
10 changes: 9 additions & 1 deletion lib/literal/types/union_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,15 @@ def >=(other)
when Literal::Types::TaggedUnionType
other.members.values.all? { |t| primitives.any? { |p| Literal.subtype?(t, p) } || types.any? { |t2| Literal.subtype?(t, t2) } }
else
types.any? { |t| Literal.subtype?(other, t) } || primitives.any? { |p| Literal.subtype?(other, p) }
primitives.any? { |p| Literal.subtype?(other, p) } || types.any? { |t| Literal.subtype?(other, t) }
end
end

def <=(other)
case other
when Module
@primitives.all? { |primitive| Literal.subtype?(primitive, other) } &&
@types.all? { |type| Literal.subtype?(type, other) }
end
end

Expand Down
32 changes: 32 additions & 0 deletions test/types/_kind.test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

include Literal::Types

test "===" do
assert numeric_kind = _Kind(Numeric)
assert numeric_kind === Numeric
assert numeric_kind === Integer
assert numeric_kind === 1
assert numeric_kind === _Integer(1..)
assert numeric_kind === _Intersection(Integer, 1..5)
assert numeric_kind === _Constraint(Integer, odd?: true)
assert numeric_kind === Float
assert numeric_kind === _Union(Integer, Float)
assert numeric_kind === _Never
assert numeric_kind === _Intersection(Integer, Numeric)
assert numeric_kind === _Deferred { Integer }

refute numeric_kind === String
refute numeric_kind === _Union(Integer, String)
refute numeric_kind === _Union(Integer, nil)
refute numeric_kind === _Nilable(Integer)
refute numeric_kind === _Intersection(String, _Any?)
refute numeric_kind === _Deferred { String }

assert _Kind(Module) === Class
assert _Kind(Module) === Module
refute _Kind(String) === Class

assert _Kind(_Array(Integer)) === _Array(1)
assert _Kind(_Array(numeric_kind.type)) === _Array(1)
end
7 changes: 7 additions & 0 deletions test/types/_never.test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,10 @@

refute _Never === nil
end

test "subtyping" do
assert_subtype _Never, Integer
assert_subtype _Never, _Union(Integer, String)
assert_subtype _Never, _Intersection(_Any?)
assert_subtype _Never, _Never
end
Loading