Class: Typisch::Type::Sequence

Inherits:
Constructor show all
Defined in:
lib/typisch/sequence.rb

Overview

A Sequence is an ordered collection of items, all of a given type.

For now if you want an unordered collection, you just have to treat it as an ordered collection with arbitrary order; if you want a map/hash you just treat it as a sequence of tuples. TODO: would be nice to have more of a hierarchy of collection types here, eg OrderedSequence < Set.

(ordered) Sequences support ‘slice types’, which are a kind of structural supertype for sequences. Their use is primarily in specifying partial serializations or partial type-checking for large sequences.

Eg sequence(:integer, :slice => 0…10)

This is saying: “A sequence of ints, which may be of any known length, but where I only care (to validate, serialize, …) at most the first 10 items”.

Eg sequence(:integer, :slice => 0…10, :total_length => false)

This is saying: “A sequence of ints, which may be of any known or unknown length, but where I only care about (validating, serializing, …) at most the first 10 items, and I don’t care about (validating, serializing…) the total length of the collection

Constant Summary collapse

VALID_IMPLEMENTATION_CLASSES =

I tried allowing any Enumerable, but this resulted in allowing String and a bunch of other things which sort of expose a vaguely-array-like interface but not really in a way that’s helpful for typing purposes. E.g. String in 1.8.7 exposes Enumerable over its lines, but an array-like interface over its characters, sometimes as strings, sometimes as ascii char codes. So not consistent at all.

Any other classes added here must expose Enumerable, but also .length and slices via [] (at least if you want them to work with slice types).

For now allowing Hashes too so they can be typed as a sequence of tuples, although should really only be typed as a set of tuples as there’s no ordering or support for slices.

[::Array, ::Hash]

Constants inherited from Constructor

Constructor::CONSTRUCTOR_TYPE_SUBCLASSES

Instance Attribute Summary collapse

Attributes inherited from Typisch::Type

#name

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Constructor

#alternative_types, inherited, #type_lattice

Methods inherited from Typisch::Type

#<, #<=, #<=>, #==, #===, #>, #>=, #alternative_types, #annotations, #annotations=, #excluding_null, #inspect, #recursive?, subtype?, #target, #to_s

Constructor Details

#initialize(type, options = {}) ⇒ Sequence

Returns a new instance of Sequence.



42
43
44
45
46
47
48
49
# File 'lib/typisch/sequence.rb', line 42

def initialize(type, options={})
  @type = type
  if options[:slice]
    @slice = options[:slice]
    @slice = (@slice.begin...@slice.end+1) unless @slice.exclude_end?
    @total_length = options[:total_length] != false
  end
end

Instance Attribute Details

#sliceObject (readonly)

Returns the value of attribute slice.



51
52
53
# File 'lib/typisch/sequence.rb', line 51

def slice
  @slice
end

#total_lengthObject (readonly)

Returns the value of attribute total_length.



51
52
53
# File 'lib/typisch/sequence.rb', line 51

def total_length
  @total_length
end

#typeObject (readonly)

Returns the value of attribute type.



91
92
93
# File 'lib/typisch/sequence.rb', line 91

def type
  @type
end

Class Method Details

.check_subtype(x, y, &recursively_check_subtype) ⇒ Object



30
31
32
33
34
35
36
37
38
39
# File 'lib/typisch/sequence.rb', line 30

def check_subtype(x, y, &recursively_check_subtype)
  recursively_check_subtype[x.type, y.type] && (
    !x.slice ||
    (y.slice && (
      x.slice.begin <= y.slice.begin &&
      x.slice.end >= y.slice.end &&
      (x.total_length || !y.total_length)
    ))
  )
end

.top_type(overall_top) ⇒ Object



26
27
28
# File 'lib/typisch/sequence.rb', line 26

def top_type(overall_top)
  new(overall_top, :slice => (0...0), :total_length => false)
end

Instance Method Details

#canonicalize!Object



102
103
104
# File 'lib/typisch/sequence.rb', line 102

def canonicalize!
  @type = @type.target
end

#check_type(instance, &recursively_check_type) ⇒ Object



61
62
63
64
65
66
67
68
# File 'lib/typisch/sequence.rb', line 61

def check_type(instance, &recursively_check_type)
  shallow_check_type(instance) && if @slice
    (instance[@slice] || []).all? {|i| recursively_check_type[@type, i]} &&
    (!@total_length || ::Integer === instance.length)
  else
    instance.all? {|i| recursively_check_type[@type, i]}
  end
end

#shallow_check_type(instance) ⇒ Object



83
84
85
# File 'lib/typisch/sequence.rb', line 83

def shallow_check_type(instance)
  case instance when *VALID_IMPLEMENTATION_CLASSES then true else false end
end

#subexpression_typesObject



57
58
59
# File 'lib/typisch/sequence.rb', line 57

def subexpression_types
  [@type]
end

#tagObject



87
88
89
# File 'lib/typisch/sequence.rb', line 87

def tag
  "Sequence"
end

#to_string(depth, indent) ⇒ Object



93
94
95
96
97
98
99
100
# File 'lib/typisch/sequence.rb', line 93

def to_string(depth, indent)
  result = "sequence(#{@type.to_s(depth+1, indent)}"
  if @slice
    result << ", :slice => #{@slice}"
    result << ", :total_length => false" unless @total_length
  end
  result << ")"
end

#with_options(options) ⇒ Object



53
54
55
# File 'lib/typisch/sequence.rb', line 53

def with_options(options)
  self.class.new(@type, {:slice => @slice, :total_length => @total_length}.merge!(options))
end