2025_06_12_Code
require './tp.rb'
tp = Tp.new
# set-up some types
tp.rules {
rule :Bool, [TrueClass, FalseClass]
rule :Nil, [NilClass]
rule :Num, [Numeric.subclasses]
rule :Enum, ->(x) {x.is_a? Enumerable}
rule :Container, [Hash, Array, Set, Struct, Data]
rule :Int, [Integer]
rule :Dec, [Float]
rule :Rat, [Rational]
rule :StrLike, [String, Symbol, Regexp]
rule :Str, [String]
}
# -------------------------------------
# Can be used to check variables
p tp.→ (3 == 3), :Bool # true
p tp.→ '1/5'.to_r, :Num # true
p tp.→ [1,2,3], :Enum # true
p tp.→ nil, :Num, :Nil # true
# '13' can't be :Num or :Bool
p tp.→ '13', *%i(Num Bool), negate: true # true
# -------------------------------------
# Can be used to prevent variables being
# assigned certain "types".. or allowing
# just some type(s).
name = 'Ior'.then { it if tp.→(it, :StrLike) }
p name # "Ior"
name2 = /Ukraine/.then { it unless tp.→(it, :StrLike, negate: true) }
p name2 # /Ukraine/
# -------------------------------------
# Can be used to assign either-or
name3 = 12.then { tp.→(it, :StrLike) ? it : :Ior }
p name3 # :Ior
# -------------------------------------
# Can be use to set other constraints on values
tp.rules {
rule :Limit, ->(x) { x.even? && x.between?(2, 20) }
}
# Nicen it up a bit
set_if = ->(var, *types, negate: false) { var if tp.→(var, *types) }
nn = set_if.('Ior', :Numeric)
nn = set_if.('Ior', :StrLike)
p nn # "Ior"
until rand_val ||= set_if.(rand(100), :Limit); end
p rand_val # 16 (example)
tp.rb
class Tp
def initialize = ( @tp = {} )
def rules(&bl) = ( instance_exec(&bl) )
def →(val, *tps, negate: false)
tps.map { check(it, val) }.send(negate ? :none? : :any?)
end
private
def rule(key, val) = ( @tp[key] = val )
def check(tp, val)
if @tp.key?(tp)
case res = @tp[tp]
when Array then res.flatten.include?(val.class)
when Proc then res.(val)
end
end
end
end