Class: Rigor::Type::Intersection

Inherits:
Object
  • Object
show all
Defined in:
lib/rigor/type/intersection.rb

Overview

‘Intersection[M1, M2, …]` — value set is the meet of every member’s value set. The carrier composes refinements that share a base, in particular the catalogued ‘non-empty-lowercase-string` (= `Difference[String, “”] & Refined[String, :lowercase]`) and `non-empty-uppercase-string` shapes from [`imported-built-in-types.md`](docs/type-specification/imported-built-in-types.md). See [ADR-3](docs/adr/3-type-representation.md) for the OQ3 working decision and the rationale for keeping Intersection a thin wrapper rather than per-shape carriers.

Construction MUST go through ‘Type::Combinator.intersection` (or the per-name factories `Combinator.non_empty_lowercase_string` / `Combinator.non_empty_uppercase_string`). The factory:

  • flattens nested intersections,

  • drops ‘Top` members (Top is the identity of intersection),

  • collapses to ‘Bot` if any member is `Bot` (Bot is absorbing),

  • deduplicates structurally-equal members,

  • sorts the surviving members by ‘describe(:short)` so two structurally-equal intersections built in different orders compare equal,

  • returns ‘Top` for the empty intersection,

  • returns the lone member for a 1-element intersection (so the carrier is never inhabited by a degenerate single-member shape).

Direct ‘.new` callers MUST pass an already-normalised member list and are expected to be tests or the combinator itself.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(members) ⇒ Intersection

Returns a new instance of Intersection.



40
41
42
43
# File 'lib/rigor/type/intersection.rb', line 40

def initialize(members)
  @members = members.dup.freeze
  freeze
end

Instance Attribute Details

#membersObject (readonly)

Returns the value of attribute members.



38
39
40
# File 'lib/rigor/type/intersection.rb', line 38

def members
  @members
end

Instance Method Details

#==(other) ⇒ Object Also known as: eql?



79
80
81
# File 'lib/rigor/type/intersection.rb', line 79

def ==(other)
  other.is_a?(Intersection) && members == other.members
end

#accepts(other, mode: :gradual) ⇒ Object



75
76
77
# File 'lib/rigor/type/intersection.rb', line 75

def accepts(other, mode: :gradual)
  Inference::Acceptance.accepts(self, other, mode: mode)
end

#botObject



67
68
69
# File 'lib/rigor/type/intersection.rb', line 67

def bot
  Trinary.no
end

#describe(verbosity = :short) ⇒ Object



45
46
47
48
49
50
# File 'lib/rigor/type/intersection.rb', line 45

def describe(verbosity = :short)
  named = canonical_name
  return named if named

  members.map { |m| m.describe(verbosity) }.join(" & ")
end

#dynamicObject



71
72
73
# File 'lib/rigor/type/intersection.rb', line 71

def dynamic
  Trinary.no
end

#erase_to_rbsObject

An intersection of refinements over the same base type erases to that base. We use the first member’s erasure because the v0.0.4 catalogue (‘non-empty-lowercase-string` etc.) is restricted to same-base composition; richer cross-base intersections will need a stricter erasure rule (likely “lowest common ancestor” via the inference engine’s class hierarchy).



59
60
61
# File 'lib/rigor/type/intersection.rb', line 59

def erase_to_rbs
  members.first.erase_to_rbs
end

#hashObject



84
85
86
# File 'lib/rigor/type/intersection.rb', line 84

def hash
  [Intersection, members].hash
end

#inspectObject



88
89
90
# File 'lib/rigor/type/intersection.rb', line 88

def inspect
  "#<Rigor::Type::Intersection #{describe(:short)}>"
end

#topObject



63
64
65
# File 'lib/rigor/type/intersection.rb', line 63

def top
  Trinary.no
end