Class: RDL::Type::FiniteHashType

Inherits:
Type
  • Object
show all
Defined in:
lib/rdl/types/finite_hash.rb

Overview

Type for finite maps from values to types; values are compared with ==. These are used for “named” arguments in Ruby, in which case the values are symbols. Finite hashes can also have a “rest” type (okay, they’re not exactly finite in this case…) which is treated as a hash from Symbol to the type.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Type

leq, #nil_type?, #optional?, #to_contract, #vararg?

Constructor Details

#initialize(elts, rest) ⇒ FiniteHashType

+ elts +

is a map from keys to types



16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/rdl/types/finite_hash.rb', line 16

def initialize(elts, rest)
  elts.each { |k, t|
    raise RuntimeError, "Got #{t.inspect} where Type expected" unless t.is_a? Type
    raise RuntimeError, "Type may not be annotated or vararg" if (t.instance_of? AnnotatedArgType) || (t.instance_of? VarargType)
  }
  @elts = elts
  @rest = rest
  @the_hash = nil
  @cant_promote = false
  @ubounds = []
  @lbounds = []
  super()
end

Instance Attribute Details

#eltsObject (readonly)

Returns the value of attribute elts.



9
10
11
# File 'lib/rdl/types/finite_hash.rb', line 9

def elts
  @elts
end

#lboundsObject

lower bounds…



13
14
15
# File 'lib/rdl/types/finite_hash.rb', line 13

def lbounds
  @lbounds
end

#restObject (readonly)

Returns the value of attribute rest.



10
11
12
# File 'lib/rdl/types/finite_hash.rb', line 10

def rest
  @rest
end

#the_hashObject (readonly)

either nil or hash type if self has been promoted to hash



11
12
13
# File 'lib/rdl/types/finite_hash.rb', line 11

def the_hash
  @the_hash
end

#uboundsObject

upper bounds this tuple has been compared with using <=



12
13
14
# File 'lib/rdl/types/finite_hash.rb', line 12

def ubounds
  @ubounds
end

Instance Method Details

#<=(other) ⇒ Object



80
81
82
# File 'lib/rdl/types/finite_hash.rb', line 80

def <=(other)
  return Type.leq(self, other)
end

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

:nodoc:



40
41
42
43
44
45
# File 'lib/rdl/types/finite_hash.rb', line 40

def ==(other) # :nodoc:
  return false if other.nil?
  return (@the_hash == other) if @the_hash
  other = other.canonical
  return (other.instance_of? FiniteHashType) && (other.elts == @elts) && (other.rest == @rest)
end

#canonicalObject



30
31
32
33
# File 'lib/rdl/types/finite_hash.rb', line 30

def canonical
  return @the_hash if @the_hash
  return self
end

#cant_promote!Object

Raises:

  • (RuntimeError)


75
76
77
78
# File 'lib/rdl/types/finite_hash.rb', line 75

def cant_promote!
  raise RuntimeError, "already promoted!" if @the_hash
  @cant_promote = true
end

#hashObject



117
118
119
120
# File 'lib/rdl/types/finite_hash.rb', line 117

def hash
  # note don't change hash value if @the_hash becomes non-nil
  return 229 * @elts.hash * @rest.hash
end

#instantiate(inst) ⇒ Object



112
113
114
115
# File 'lib/rdl/types/finite_hash.rb', line 112

def instantiate(inst)
  return @the_hash.instantiate(inst) if @the_hash
  return FiniteHashType.new(Hash[@elts.map { |k, t| [k, t.instantiate(inst)] }], (if @rest then @rest.instantiate(inst) end))
end

#match(other) ⇒ Object



49
50
51
52
53
54
55
56
57
58
59
# File 'lib/rdl/types/finite_hash.rb', line 49

def match(other)
  return @the_hash.match(other) if @the_hash
  other = other.canonical
  other = other.type if other.instance_of? AnnotatedArgType
  return true if other.instance_of? WildQuery
  return false unless other.instance_of? FiniteHashType
  return false unless ((@rest.nil? && other.rest.nil?) ||
                       (!@rest.nil? && !other.rest.nil? && @rest.match(other.rest)))
  return (@elts.length == other.elts.length &&
          @elts.all? { |k, v| (other.elts.has_key? k) && (v.match(other.elts[k]))})
end

#member?(obj, *args) ⇒ Boolean

Returns:

  • (Boolean)


84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/rdl/types/finite_hash.rb', line 84

def member?(obj, *args)
  return @the_hash.member(obj, *args) if @the_hash
  t = RDL::Util.rdl_type obj
  return t <= self if t
  right_elts = @elts.clone # shallow copy

  return false unless obj.instance_of? Hash

  # Check that every mapping in obj exists in @map and matches the type
  obj.each_pair { |k, v|
    if @elts.has_key? k
      t = @elts[k]
      t = t.type if t.instance_of? OptionalType
      return false unless t.member? v
      right_elts.delete k
    else
      return false unless @rest && @rest.member?(v)
    end
  }

  # Check that any remaining types are optional
  right_elts.each_pair { |k, vt|
    return false unless vt.instance_of? OptionalType
  }

  return true
end

#promote!Object



61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/rdl/types/finite_hash.rb', line 61

def promote!
  return false if @cant_promote
  # TODO look at key types
  domain_type = UnionType.new(*(@elts.keys.map { |k| NominalType.new(k.class) }))
  range_type = UnionType.new(*@elts.values)
  if @rest
    domain_type = UnionType.new(domain_type, RDL::Globals.types[:symbol])
    range_type = UnionType.new(range_type, @rest)
  end
  @the_hash = GenericType.new(RDL::Globals.types[:hash], domain_type, range_type)
  # same logic as Tuple
  return (@lbounds.all? { |lbound| lbound <= self }) && (@ubounds.all? { |ubound| self <= ubound })
end

#to_sObject



35
36
37
38
# File 'lib/rdl/types/finite_hash.rb', line 35

def to_s
  return @the_hash.to_s if @the_hash
  return "{ " + @elts.map { |k, t| k.to_s + ": " + t.to_s }.join(', ') + (if @rest then ", **" + @rest.to_s else "" end) + " }"
end