# encoding: utf-8

module Axiom
  module Types

    # Abstract base class for every type
    class Type
      extend Options, DescendantsTracker

      accept_options :constraint
      constraint proc { true }

      def self.new
        raise NotImplementedError, "#{inspect} should not be instantiated"
      end

      def self.constraint(constraint = Undefined, &block)
        constraint = block if constraint.equal?(Undefined)
        current    = @constraint
        return current if constraint.nil?
        @constraint = if current
          lambda { |object| current.call(object) && constraint.call(object) }
        else
          constraint
        end
        self
      end

      def self.includes(*set)
        # TODO: benchmark the cross-over point where it is more efficient to use
        # a Set than an Array, and then consider conditionally converting?
        set.uniq!
        constraint { |object| set.include?(object) }
      end

      def self.include?(object)
        included = constraint.call(object)
        if included != true && included != false
          raise TypeError,
            "constraint must return true or false, but was #{included.inspect}"
        end
        included
      end

      def self.finalize
        IceNine.deep_freeze(self)
      end

      def self.finalized?
        frozen?
      end

    end # class Type
  end # module Types
end # module Axiom

# encoding: utf-8

module Axiom
  module Types

    # Abstract base class for every type
    class Type
      extend Options, DescendantsTracker

      accept_options :constraint
      constraint proc { true }

      def self.new
        raise NotImplementedError, "#{inspect} should not be instantiated"
      end

      def self.constraint(constraint = Undefined, &block)
        constraint = block if constraint.equal?(Undefined)
        current    = @constraint
        return current if constraint.nil?
        @constraint = if current
          lambda { |object| current.call(object) && constraint.call(object) }
        else
          constraint
        end
        self
      end

      def self.includes(*members)
        set = IceNine.deep_freeze(members.to_set)
        constraint { |object| set.include?(object) }
      end

      def self.include?(object)
        included = constraint.call(object)
        if included != true && included != false
          raise TypeError,
            "constraint must return true or false, but was #{included.inspect}"
        end
        included
      end

      def self.finalize
        IceNine.deep_freeze(self)
      end

      def self.finalized?
        frozen?
      end

    end # class Type
  end # module Types
end # module Axiom

Change the members to be a frozen set
Change the members to be a frozen set

* I benchmarked the difference between Array#include? and
  Set#include? and the latter was faster even for small sets of
  objects. The performance was outstanding for larger sets.

  It makes sense since Array#include? is O(n) and Set#include? is
  O(1), yet I thought I has read somewhere that Array#include? was
  faster when "n" was small, but that does not seem to be the case,
  at least under ruby 1.9.3.
