lib/kind.rb in kind-1.5.0 vs lib/kind.rb in kind-1.6.0

- old
+ new

@@ -1,40 +1,69 @@ # frozen_string_literal: true require 'kind/version' + +require 'kind/empty' require 'kind/undefined' require 'kind/maybe' + require 'kind/error' +require 'kind/of' require 'kind/is' require 'kind/checker' -require 'kind/of' require 'kind/types' module Kind - def self.is; Is; end - def self.of; Of; end + WRONG_NUMBER_OF_ARGUMENTS = 'wrong number of arguments (given 1, expected 2)'.freeze - # -- Classes - [ - String, Symbol, Numeric, Integer, Float, Regexp, Time, - Array, Range, Hash, Struct, Enumerator, - Method, Proc, - IO, File - ].each { |klass| Types.add(klass) } + private_constant :WRONG_NUMBER_OF_ARGUMENTS - Types.add(Queue, name: 'Queue'.freeze) + def self.is(expected = Undefined, object = Undefined) + return Is if expected == Undefined && object == Undefined - # -- Modules - [ - Enumerable, Comparable - ].each { |klass| Types.add(klass) } + return Kind::Is.(expected, object) if object != Undefined + raise ArgumentError, WRONG_NUMBER_OF_ARGUMENTS + end + + MODULE_OR_CLASS = 'Module/Class'.freeze + + private_constant :MODULE_OR_CLASS + + def self.of(kind = Undefined, object = Undefined) + return Of if kind == Undefined && object == Undefined + + return Kind::Of.(kind, object) if object != Undefined + + __checkers__[kind] ||= begin + kind_name = kind.name + + if Kind::Of.const_defined?(kind_name, false) + Kind::Of.const_get(kind_name) + else + Checker.new(Kind::Of.(Module, kind, MODULE_OR_CLASS)) + end + end + end + + private_class_method def self.__checkers__ + @__checkers__ ||= {} + end + # --------------------- # # Special type checkers # # --------------------- # module Is + def self.Class(value) + value.is_a?(::Class) + end + + def self.Module(value) + value == ::Module || (value.is_a?(::Module) && !self.Class(value)) + end + def self.Boolean(value) klass = Kind.of.Class(value) klass <= TrueClass || klass <= FalseClass end @@ -42,13 +71,77 @@ value.respond_to?(:call) || (value.is_a?(Module) && value.public_instance_methods.include?(:call)) end end module Of + # -- Class + + def self.Class(object = Undefined) + return Class if object == Undefined + + self.call(::Class, object) + end + + const_set(:Class, ::Module.new do + extend Checkable + + def self.__kind; ::Class; end + + def self.class?(value); Kind::Is.Class(value); end + + def self.instance?(value); class?(value); end + end) + + # -- Module + + def self.Module(object = Undefined) + return Module if object == Undefined + + self.call(::Module, object) + end + + const_set(:Module, ::Module.new do + extend Checkable + + def self.__kind; ::Module; end + + def self.__kind_error(value) + raise Kind::Error.new('Module'.freeze, value) + end + + def self.__kind_of(value) + return value if Kind::Is.Module(value) + + __kind_error(value) + end + + def self.__kind_undefined(value) + __kind_error(Kind::Undefined) if value == Kind::Undefined + + yield + end + + def self.class?(value); Kind::Is.Module(value); end + + def self.instance(value, options = Empty::HASH) + default = options[:or] + + if ::Kind::Maybe::Value.none?(default) + __kind_undefined(value) { __kind_of(value) } + else + return value if instance?(value) + + __kind_undefined(default) { __kind_of(default) } + end + end + + def self.instance?(value); class?(value); end + end) + # -- Boolean - def self.Boolean(object = Undefined, options = {}) + def self.Boolean(object = Undefined, options = Empty::HASH) default = options[:or] return Kind::Of::Boolean if object == Undefined && default.nil? bool = object.nil? ? default : object @@ -57,24 +150,49 @@ raise Kind::Error.new('Boolean'.freeze, bool) end const_set(:Boolean, ::Module.new do - extend Checker + extend Checkable def self.__kind; [TrueClass, FalseClass].freeze; end def self.class?(value); Kind.is.Boolean(value); end + def self.instance(value, options= Empty::HASH) + default = options[:or] + + if ::Kind::Maybe::Value.none?(default) + __kind_undefined(value) { Kind::Of::Boolean(value) } + else + return value if instance?(value) + + __kind_undefined(default) { Kind::Of::Boolean(default) } + end + end + + def self.__kind_undefined(value) + if value == Kind::Undefined + raise Kind::Error.new('Boolean'.freeze, Kind::Undefined) + else + yield + end + end + def self.instance?(value); value.is_a?(TrueClass) || value.is_a?(FalseClass) end + + def self.or_undefined(value) + result = or_nil(value) + result.nil? ? Kind::Undefined : result + end end) # -- Lambda - def self.Lambda(object = Undefined, options = {}) + def self.Lambda(object = Undefined, options = Empty::HASH) default = options[:or] return Kind::Of::Lambda if object == Undefined && default.nil? func = object || default @@ -83,22 +201,42 @@ raise Kind::Error.new('Lambda'.freeze, func) end const_set(:Lambda, ::Module.new do - extend Checker + extend Checkable def self.__kind; ::Proc; end + def self.instance(value, options = Empty::HASH) + default = options[:or] + + if ::Kind::Maybe::Value.none?(default) + __kind_undefined(value) { Kind::Of::Lambda(value) } + else + return value if instance?(value) + + __kind_undefined(default) { Kind::Of::Lambda(default) } + end + end + + def self.__kind_undefined(value) + if value == Kind::Undefined + raise Kind::Error.new('Lambda'.freeze, Kind::Undefined) + else + yield + end + end + def self.instance?(value) value.is_a?(__kind) && value.lambda? end end) # -- Callable - def self.Callable(object = Undefined, options = {}) + def self.Callable(object = Undefined, options = Empty::HASH) default = options[:or] return Kind::Of::Callable if object == Undefined && default.nil? callable = object || default @@ -107,19 +245,63 @@ raise Kind::Error.new('Callable'.freeze, callable) end const_set(:Callable, ::Module.new do - extend Checker + extend Checkable def self.__kind; Object; end def self.class?(value) Kind::Is::Callable(value) end + def self.instance(value, options = Empty::HASH) + default = options[:or] + + if ::Kind::Maybe::Value.none?(default) + __kind_undefined(value) { Kind::Of::Callable(value) } + else + return value if instance?(value) + + __kind_undefined(default) { Kind::Of::Callable(default) } + end + end + + def self.__kind_undefined(value) + if value == Kind::Undefined + raise Kind::Error.new('Callable'.freeze, Kind::Undefined) + else + yield + end + end + def self.instance?(value); value.respond_to?(:call) end end) + + # ---------------------- # + # Built-in type checkers # + # ---------------------- # + + # -- Classes + [ + String, Symbol, Numeric, Integer, Float, Regexp, Time, + Array, Range, Hash, Struct, Enumerator, Set, + Method, Proc, + IO, File + ].each { |klass| Types.add(klass) } + + Types.add(Queue, name: 'Queue'.freeze) + + # -- Modules + [ + Enumerable, Comparable + ].each { |klass| Types.add(klass) } + + # -- Kind::Of::Maybe + + Types.add(Kind::Maybe::Result, name: 'Maybe'.freeze) + Types.add(Kind::Maybe::Result, name: 'Optional'.freeze) end end