Class: Finitio::AdType

Inherits:
Type
  • Object
show all
Defined in:
lib/finitio/type/ad_type.rb,
lib/finitio/generation/ad_type.rb,
lib/finitio/json_schema/ad_type.rb

Overview

The Abtract/Algebraic Data Type is a generator to build user-defined information types in a more abstract way than using sub types. For instance, a Color could be defined as follows:

Color := <Rgb> {r: Byte, g: Byte, b: Byte},
         <Hex> String( s | s =~ /^#[0-9a-f]{6}$/i )

Such a declaration does not define a new type by subtyping but instead declares so-called possible representations for the color. Here, two possible representations are defined: one is a rgb triple through a Tuple type, the other is an hexadecimal notation through a subset of String.

Given an AdType, Finitio requires special dress/undress function pairs to encode/decode from the type to the concrete representations and vice versa. This pair of functions is called the “information contract”.

This class allows capturing such information types. For instance,

# This is the concrete representation of Finitio's Color, through a
# usual Ruby ADT (we call it ColorImpl here to avoid confusion, but in
# practice one would simply call it Color).
class ColorImpl
  # ... your usual color implementation
end

# The RGB info type: {r: Byte, g: Byte, b: Byte}
rgb_infotype  = TupleType.new(...)

# The RGB contract converter, an object that responds to `call` to
# convert from a valid Hash[r: Integer, ...] to a ColorImpl instance.
rgb_contract = ...

AdType.new(ColorImpl, rgb: [rgb_infotype, rgb_contract], hex: ...)

The ruby ADT class is of course used as concrete representation of the AdType, that is,

R(Color) = ColorImpl

Accordingly, the ‘dress` transformation function has the following signature:

dress :: Alpha  -> Color     throws TypeError
dress :: Object -> ColorImpl throws TypeError

Constant Summary

Constants included from Metadata

Metadata::EMPTY_METADATA

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Type

#anonymous?, #name, #name=, #named?, #suppremum, #to_s

Methods included from Metadata

#metadata, #metadata=, #metadata?

Constructor Details

#initialize(ruby_type, contracts, name = nil, metadata = nil) ⇒ AdType

Returns a new instance of AdType.



51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/finitio/type/ad_type.rb', line 51

def initialize(ruby_type, contracts, name = nil,  = nil)
  unless ruby_type.nil? or ruby_type.is_a?(Module)
    raise ArgumentError, "Module expected, got `#{ruby_type}`"
  end
  unless contracts.is_a?(Array) &&
         contracts.all?{|c| c.is_a?(Contract) }
    raise ArgumentError, "[Contract] expected, got `#{contracts}`"
  end

  super(name, )
  @ruby_type = ruby_type
  @contracts = contracts.freeze
end

Instance Attribute Details

#contractsObject (readonly)

Returns the value of attribute contracts.



64
65
66
# File 'lib/finitio/type/ad_type.rb', line 64

def contracts
  @contracts
end

#ruby_typeObject Also known as: representator

Returns the value of attribute ruby_type.



64
65
66
# File 'lib/finitio/type/ad_type.rb', line 64

def ruby_type
  @ruby_type
end

Instance Method Details

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



138
139
140
141
142
143
144
# File 'lib/finitio/type/ad_type.rb', line 138

def ==(other)
  super || (
    other.is_a?(AdType) &&
    ruby_type == other.ruby_type &&
    contracts == other.contracts
  )
end

#[](name) ⇒ Object



75
76
77
# File 'lib/finitio/type/ad_type.rb', line 75

def [](name)
  contracts.find{|c| c.name == name }
end

#contract_namesObject



79
80
81
# File 'lib/finitio/type/ad_type.rb', line 79

def contract_names
  contracts.map(&:name)
end

#default_nameObject



83
84
85
# File 'lib/finitio/type/ad_type.rb', line 83

def default_name
  (ruby_type && ruby_type.name.to_s) || "Anonymous"
end

#dress(value, handler = DressHelper.new) ⇒ Object



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/finitio/type/ad_type.rb', line 91

def dress(value, handler = DressHelper.new)
  # Up should be idempotent with respect to the ADT
  return value if ruby_type and value.is_a?(ruby_type)

  # Dressed value and first exception
  dressed, error = nil, nil

  # Try each contract in turn. Do nothing on TypeError as
  # the next candidate could be the good one! Return the
  # first successfully dressed.
  contracts.each do |contract|

    # First make the dress transformation on the information type
    success, dressed = handler.just_try do
      contract.infotype.dress(value, handler)
    end

    # Save very first error on failure
    unless success
      error ||= dressed
      next
    end

    # Seems nice, just try to get one stage higher now
    success, dressed = handler.just_try(StandardError) do
      contract.dresser.call(dressed)
    end

    if success
      if ruby_type && !dressed.is_a?(ruby_type)
        raise "Invalid IC dresser (#{contract.dresser}):"\
              " #{ruby_type} expected, got #{dressed.class}"
      end
      return dressed
    else
      error ||= dressed
    end
  end

  # No one succeeded, just fail
  handler.failed!(self, value, error)
end

#generate_data(generator, world = nil) ⇒ Object



4
5
6
7
# File 'lib/finitio/generation/ad_type.rb', line 4

def generate_data(generator, world = nil)
  infotype = generator.flip_one_out_of(contracts).infotype
  generator.call(infotype, world)
end

#hashObject



134
135
136
# File 'lib/finitio/type/ad_type.rb', line 134

def hash
  ruby_type.hash ^ contracts.hash
end

#include?(value) ⇒ Boolean

Returns:

  • (Boolean)


87
88
89
# File 'lib/finitio/type/ad_type.rb', line 87

def include?(value)
  ruby_type === value
end

#resolve_proxies(system) ⇒ Object



147
148
149
# File 'lib/finitio/type/ad_type.rb', line 147

def resolve_proxies(system)
  AdType.new(ruby_type, contracts.map{|t| t.resolve_proxies(system)}, name, )
end

#to_json_schema(*args, &bl) ⇒ Object



4
5
6
7
8
# File 'lib/finitio/json_schema/ad_type.rb', line 4

def to_json_schema(*args, &bl)
  {
    anyOf: contracts.map{|c| c.infotype.to_json_schema(*args, &bl) }
  }
end

#unconstrainedObject



151
152
153
# File 'lib/finitio/type/ad_type.rb', line 151

def unconstrained
  AdType.new(ruby_type, contracts.map{|c| c.unconstrained}, name, )
end