Class: Qrb::AdType
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, Q 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 Q'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: Fixnum, ...] 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
Instance Attribute Summary collapse
-
#contracts ⇒ Object
readonly
Returns the value of attribute contracts.
-
#ruby_type ⇒ Object
readonly
Returns the value of attribute ruby_type.
Instance Method Summary collapse
- #contract_names ⇒ Object
- #default_name ⇒ Object
- #dress(value, handler = DressHelper.new) ⇒ Object
- #include?(value) ⇒ Boolean
-
#initialize(ruby_type, contracts, name = nil) ⇒ AdType
constructor
A new instance of AdType.
Methods inherited from Type
Constructor Details
#initialize(ruby_type, contracts, name = nil) ⇒ AdType
Returns a new instance of AdType.
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/qrb/type/ad_type.rb', line 51 def initialize(ruby_type, contracts, name = nil) unless ruby_type.nil? or ruby_type.is_a?(Module) raise ArgumentError, "Module expected, got `#{ruby_type}`" end unless contracts.is_a?(Hash) raise ArgumentError, "Hash expected, got `#{contracts}`" end invalid = contracts.values.reject{|v| v.is_a?(Array) and v.size == 3 and v[0].is_a?(Type) and \ v[1].respond_to?(:call) and v[2].respond_to?(:call) } unless invalid.empty? raise ArgumentError, "Invalid contracts `#{invalid}`" end super(name) @ruby_type = ruby_type @contracts = contracts.freeze end |
Instance Attribute Details
#contracts ⇒ Object (readonly)
Returns the value of attribute contracts.
70 71 72 |
# File 'lib/qrb/type/ad_type.rb', line 70 def contracts @contracts end |
#ruby_type ⇒ Object (readonly)
Returns the value of attribute ruby_type.
70 71 72 |
# File 'lib/qrb/type/ad_type.rb', line 70 def ruby_type @ruby_type end |
Instance Method Details
#contract_names ⇒ Object
72 73 74 |
# File 'lib/qrb/type/ad_type.rb', line 72 def contract_names contracts.keys end |
#default_name ⇒ Object
76 77 78 |
# File 'lib/qrb/type/ad_type.rb', line 76 def default_name (ruby_type && ruby_type.name.to_s) || "Anonymous" end |
#dress(value, handler = DressHelper.new) ⇒ Object
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 |
# File 'lib/qrb/type/ad_type.rb', line 84 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) # 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_pair do |name, (infotype, dresser, _)| # First make the dress transformation on the information type success, dressed = handler.just_try do infotype.dress(value, handler) end next unless success # Seems nice, just try to get one stage higher now success, dressed = handler.just_try(StandardError) do dresser.call(dressed) end return dressed if success end # No one succeeded, just fail handler.failed!(self, value) end |
#include?(value) ⇒ Boolean
80 81 82 |
# File 'lib/qrb/type/ad_type.rb', line 80 def include?(value) ruby_type === value end |