class Dry::Logic::Rule

def [](*input)

def [](*input)
  arity == 0 ? predicate.() : predicate[*args, *input]
end

def args_with_names(*input)

def args_with_names(*input)
  parameters.map(&:last).zip(args + input)
end

def ast(input = Undefined)

def ast(input = Undefined)
  [:predicate, [id, args_with_names(input)]]
end

def bind(object)

def bind(object)
  if UnboundMethod === predicate
    self.class.new(predicate.bind(object), options)
  else
    self.class.new(
      -> *args { object.instance_exec(*args, &predicate) },
      options.merge(arity: arity, parameters: parameters)
    )
  end
end

def call(*input)

def call(*input)
  Result.new(self[*input], id) { ast(*input) }
end

def curry(*new_args)

def curry(*new_args)
  all_args = args + new_args
  if all_args.size > arity
    raise ArgumentError, "wrong number of arguments (#{all_args.size} for #{arity})"
  else
    with(args: all_args)
  end
end

def eval_args(object)

def eval_args(object)
  with(args: args.map { |arg| UnboundMethod === arg ? arg.bind(object).() : arg })
end

def id

def id
  options[:id]
end

def initialize(predicate, options = DEFAULT_OPTIONS)

def initialize(predicate, options = DEFAULT_OPTIONS)
  @predicate = predicate
  @options = options
  @args = options[:args]
  @arity = options[:arity] || predicate.arity
end

def parameters

def parameters
  options[:parameters] || predicate.parameters
end

def type

def type
  :rule
end

def with(new_opts)

def with(new_opts)
  self.class.new(predicate, options.merge(new_opts))
end