lib/finitio/type/struct_type.rb
module Finitio
#
# The Struct type generator allows capturing positional sequences of values,
# such as pairs, triples and so forth. For instance, a Point type could be
# defined as follows:
#
# Point = <Length, Angle>
#
# This class allows capturing those information types, as in:
#
# Length = BuiltinType.new(Integer)
# Angle = BuiltinType.new(Float)
# Point = StructType.new([Length, Angle])
#
# A simple Array is used as concrete ruby representation for structs. The
# values map to the concrete representations of each component type:
#
# R(Point) = Array[R(Length) ^ R(Angle)]
# = Array[Integer ^ Float]
# = Array[Numeric]
#
# where `^` denotes the `least common super type` operator on ruby classes.
#
# Accordingly, the `dress` transformation function has the signature below.
# It expects it's Alpha/Object argument to be an Array with all and only the
# expected components. The `dress` function applies on every component
# according to its type.
#
# dress :: Alpha -> Point throws TypeError
# dress :: Object -> Array[Numeric] throws TypeError
#
class StructType < Type
def initialize(component_types, name = nil, metadata = nil)
unless component_types.is_a?(Array) &&
component_types.all?{|c| c.is_a?(Type) }
raise ArgumentError, "[Finitio::Type] expected, got `#{component_types}`"
end
super(name, metadata)
@component_types = component_types
end
attr_reader :component_types
def representator
component_types.map(&:representator)
end
def default_name
"<" + @component_types.map(&:name).join(', ') + ">"
end
def include?(value)
value.is_a?(Array) &&
value.size==component_types.size &&
value.zip(component_types).all?{|v,t| t.include?(v) }
end
def dress(value, handler = DressHelper.new)
handler.failed!(self, value) unless value.is_a?(Array)
# check the size
cs, vs = component_types.size, value.size
handler.fail!("Struct size mismatch (#{vs} for #{cs})") unless cs==vs
# dress components
array = []
handler.iterate(value) do |elm, index|
array << component_types[index].dress(elm, handler)
end
array
end
def ==(other)
super || (other.is_a?(StructType) && other.component_types == component_types)
end
alias :eql? :==
def hash
self.class.hash ^ component_types.hash
end
def resolve_proxies(system)
StructType.new(component_types.map{|t| t.resolve_proxies(system)}, name, metadata)
end
def unconstrained
StructType.new(component_types.map{|t| t.unconstrained}, name, metadata)
end
end # class StructType
end # module Finitio