Module: Literal

Defined in:
lib/literal.rb,
lib/literal/version.rb

Defined Under Namespace

Modules: Error, Properties, Rails, Type, Types, Undefined Classes: ArgumentError, Array, Data, DataStructure, Delegator, Enum, Failure, Flags, Flags16, Flags32, Flags64, Flags8, Hash, Object, Property, Railtie, Result, ResultHandler, Set, Struct, SubtypeContext, Success, Tuple, TypeError, Value

Constant Summary collapse

Loader =
Zeitwerk::Loader.for_gem.tap do |loader|
	loader.ignore("#{__dir__}/literal/rails")
	loader.ignore("#{__dir__}/literal/railtie.rb")
	loader.ignore("#{__dir__}/ruby_lsp")

	loader.inflector.inflect(
		"json_data_type" => "JSONDataType"
	)

	loader.collapse("#{__dir__}/literal/flags")
	loader.collapse("#{__dir__}/literal/errors")

	loader.setup
end
VERSION =
"1.9.0"
Transforms =

A map of core types to transform Procs mapping to the new type.

{
	Integer => {
		abs: Integer,
		ceil: Integer,
		chr: String,
		denominator: Integer,
		even?: Literal::Types::BooleanType::Instance,
		floor: Integer,
		hash: Integer,
		inspect: String,
		integer?: true,
		magnitude: Integer,
		negative?: Literal::Types::BooleanType::Instance,
		next: Integer,
		numerator: Integer,
		odd?: Literal::Types::BooleanType::Instance,
		ord: Integer,
		positive?: Literal::Types::BooleanType::Instance,
		pred: Integer,
		round: Integer,
		size: Integer,
		succ: Integer,
		to_f: Float,
		to_i: Integer,
		to_int: Integer,
		to_r: Rational,
		to_s: String,
		truncate: Integer,
		zero?: Literal::Types::BooleanType::Instance,
	},
	String => {
		ascii_only?: Literal::Types::BooleanType::Instance,
		bytesize: Integer,
		capitalize: String,
		chomp: String,
		chop: String,
		downcase: String,
		dump: String,
		empty?: Literal::Types::BooleanType::Instance,
		hash: Integer,
		inspect: String,
		length: Integer,
		lstrip: String,
		ord: Integer,
		reverse: String,
		rstrip: String,
		scrub: String,
		size: Integer,
		strip: String,
		swapcase: String,
		to_str: String,
		upcase: String,
		valid_encoding?: Literal::Types::BooleanType::Instance,
		to_i: Integer,
	},
	Numeric => {
		to_i: Integer,
		to_f: Float,
		to_s: String,
	},
	Array => {
		size: Integer,
		length: Integer,
		empty?: Literal::Types::BooleanType::Instance,
		sort: Array,
		to_a: Array,
		to_ary: Array,
	},
	Hash => {
		empty?: Literal::Types::BooleanType::Instance,
		inspect: String,
		keys: Array,
		length: Integer,
		size: Integer,
		to_a: Array,
		to_h: Hash,
		to_s: String,
		values: Array,
	},
	Set => {
		empty?: Literal::Types::BooleanType::Instance,
		inspect: String,
		length: Integer,
		size: Integer,
		to_a: Array,
		to_s: String,
	},
	Float => {
		abs: Float,
		ceil: Integer,
		floor: Integer,
		nan?: Literal::Types::BooleanType::Instance,
		negative?: Literal::Types::BooleanType::Instance,
		positive?: Literal::Types::BooleanType::Instance,
		round: Integer,
		to_i: Integer,
		to_s: String,
		truncate: Integer,
		zero?: Literal::Types::BooleanType::Instance,
	},
	Symbol => {
		empty?: Literal::Types::BooleanType::Instance,
		inspect: String,
		length: Integer,
		size: Integer,
		to_s: String,
		to_sym: Symbol,
	},
	Range => {
		begin: Object,
		end: Object,
		exclude_end?: Literal::Types::BooleanType::Instance,
		first: Object,
		last: Object,
		max: Object,
		min: Object,
		size: Integer,
		to_a: Array,
		to_s: String,
	},
	Regexp => {
		casefold?: Literal::Types::BooleanType::Instance,
		inspect: String,
		source: String,
		to_s: String,
	},
	Time => {
		day: Integer,
		hour: Integer,
		inspect: String,
		min: Integer,
		month: Integer,
		sec: Integer,
		to_a: Array,
		to_f: Float,
		to_i: Integer,
		to_s: String,
		year: Integer,
	},
}.transform_values! { |it| it.transform_keys(&:to_proc).freeze }.freeze

Class Method Summary collapse

Class Method Details

.Array(type) ⇒ Object



64
65
66
# File 'lib/literal.rb', line 64

def self.Array(type)
	Literal::Array::Generic.new(type)
end

.BrandObject



80
81
82
# File 'lib/literal.rb', line 80

def self.Brand(...)
	Literal::Brand.new(...)
end

.check(value, type) ⇒ Object



114
115
116
117
118
119
120
121
122
123
# File 'lib/literal.rb', line 114

def self.check(value, type)
	if type === value
		true
	else
		context = Literal::TypeError::Context.new(expected: type, actual: value)
		type.record_literal_type_errors(context) if type.respond_to?(:record_literal_type_errors)
		yield context if block_given?
		raise Literal::TypeError.new(context:)
	end
end

.Delegator(*args, **kwargs, &block) ⇒ Object



48
49
50
51
52
53
54
55
56
# File 'lib/literal.rb', line 48

def self.Delegator(*args, **kwargs, &block)
	delegator_class = Class.new(Literal::Delegator)

	type = Literal::Types._Constraint(*args, **kwargs)
	delegator_class.define_method(:__type__) { type }

	delegator_class.class_eval(&block) if block
	delegator_class.freeze
end

.Enum(type) ⇒ Object



58
59
60
61
62
# File 'lib/literal.rb', line 58

def self.Enum(type)
	Class.new(Literal::Enum) do
		prop :value, type, :positional, reader: :public
	end
end

.Failure(type) ⇒ Object



110
111
112
# File 'lib/literal.rb', line 110

def self.Failure(type)
	Failure::Generic.new(type)
end

.Hash(key_type, value_type) ⇒ Object



72
73
74
# File 'lib/literal.rb', line 72

def self.Hash(key_type, value_type)
	Literal::Hash::Generic.new(key_type, value_type)
end

.Result(success_type, failure_type) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/literal.rb', line 84

def self.Result(success_type, failure_type)
	result_type = Result::Generic.new(success_type, failure_type)

	if block_given?
		caught = catch do |ball|
			emitter = Result::Emitter.new(type: result_type, ball:)
			yield(emitter)
		end

		case caught
		when Result::Thrown
			Literal.check(caught.result, result_type)
			caught.result
		else
			Literal.check(caught, success_type)
			result_type.success(caught)
		end
	else
		result_type
	end
end

.Set(type) ⇒ Object



68
69
70
# File 'lib/literal.rb', line 68

def self.Set(type)
	Literal::Set::Generic.new(type)
end

.subtype?(type, supertype, context: nil) ⇒ Boolean

Returns:

  • (Boolean)


125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/literal.rb', line 125

def self.subtype?(type, supertype, context: nil)
	context ||= SubtypeContext.new
	raw_key = [type.object_id, supertype.object_id]
	raw_acquired = false
	resolved_key = nil
	resolved_acquired = false

	return context.fetch(raw_key) if context.memoized?(raw_key)
	return false unless context.acquire(raw_key)

	raw_acquired = true

	subtype = type

	subtype = subtype.block.call if Types::DeferredType === subtype
	supertype = supertype.block.call if Types::DeferredType === supertype
	resolved_key = [subtype.object_id, supertype.object_id]

	if resolved_key != raw_key
		if context.memoized?(resolved_key)
			result = context.fetch(resolved_key)
			context.store(raw_key, result)
			return result
		end

		return false unless context.acquire(resolved_key)

		resolved_acquired = true
	end

	result = if subtype == Types::NeverType::Instance
		true
	elsif supertype == subtype
		true
	else
		case supertype
		when Literal::Type
			supertype.>=(subtype, context:)
		when Module
			case subtype
			when Module
				supertype >= subtype
			when Numeric
				Numeric >= supertype
			when String
				String >= supertype
			when Symbol
				Symbol >= supertype
			when ::Array
				::Array >= supertype
			when ::Hash
				::Hash >= supertype
			when Literal::Type
				subtype.<=(supertype, context:)
			else
				false
			end
		when Range
			supertype.cover?(subtype)
		else
			false
		end
	end

	context.store(resolved_key, result)
	context.store(raw_key, result)
	result
ensure
	if context
		context.release(resolved_key) if resolved_acquired
		context.release(raw_key) if raw_acquired
	end
end

.Success(type) ⇒ Object



106
107
108
# File 'lib/literal.rb', line 106

def self.Success(type)
	Success::Generic.new(type)
end

.Tuple(*types) ⇒ Object



76
77
78
# File 'lib/literal.rb', line 76

def self.Tuple(*types)
	Literal::Tuple::Generic.new(*types)
end

.Value(*args, **kwargs, &block) ⇒ Object



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/literal.rb', line 22

def self.Value(*args, **kwargs, &block)
	value_class = Class.new(Literal::Value)

	type = Literal::Types._Constraint(*args, **kwargs)
	value_class.define_method(:__type__) { type }

	if subtype?(type, Integer)
		value_class.alias_method :to_i, :value
	elsif subtype?(type, String)
		value_class.alias_method :to_s, :value
		value_class.alias_method :to_str, :value
	elsif subtype?(type, Array)
		value_class.alias_method :to_a, :value
		value_class.alias_method :to_ary, :value
	elsif subtype?(type, Hash)
		value_class.alias_method :to_h, :value
	elsif subtype?(type, Float)
		value_class.alias_method :to_f, :value
	elsif subtype?(type, Set)
		value_class.alias_method :to_set, :value
	end

	value_class.class_eval(&block) if block
	value_class.freeze
end