# = functor.rb
#
# == Copyright (c) 2004 Thomas Sawyer
#
#   Ruby License
#
#   This module is free software. You may use, modify, and/or redistribute this
#   software under the same terms as Ruby.
#
#   This program is distributed in the hope that it will be useful, but WITHOUT
#   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
#   FOR A PARTICULAR PURPOSE.
#
# == Developer Notes
#
# TODO Consider renaming Functor to Dispatch or Dispatcher, or Func ?
#
# == Author(s)
#
# * Thomas Sawyer

# CREDIT Thomas Sawyer
#
# LOG 2006-11-04 trans Made Functor a subclass of Proc rather then use delgation.
# LOG 2006-11-04 trans Deprecated #self. Call #self on the #binding instead.
# LOG 2006-11-04 trans Deprecated #functor_function, renamed to #to_proc.
# LOG 2006-11-04 trans Deprecated #functor_passthru, renamed to #to_a.
# LOG 2006-11-04 trans All methods are now private except #binding and operators.

# Author:: Thomas Sawyer
# Copyright:: Copyright (c) 2006 Thomas Sawyer
# License:: Ruby License

require 'facets/core/binding/self'

# = Functor
#
# By definition a Functor is simply a first class method, but these are common
# in the form of Method and Proc. So here a Functor is a bit more specialized
# as a 1st class _metafunction_. Essentally, a Functor can vary its behavior
# accorrding to the operation applied to it.
#
# == Synopsis
#
#   f = Functor.new { |op, x| x.send(op, x) }
#   f + 1  #=> 2
#   f + 2  #=> 4
#   f + 3  #=> 6
#   f * 1  #=> 1
#   f * 2  #=> 4
#   f * 3  #=> 9
#

class Functor < Proc

  # Privatize all methods except #binding an operators.

  private *instance_methods.select { |m| m !~ /(^__|^\W|^binding$)/ }

  # Functor is created with a block the first argument of which is always
  # the operator/method the functor will recieve. You can pass thru
  # additional arguments along with the function that will be passed
  # to the function as the initial parameters after the operator.

  def initialize(*arguments, &procedure)
    @arguments = arguments
    @procedure = procedure
    super(&procedure)
  end

  # Any acton against the functor is processesd by the function.

  def method_missing(op, *args, &blk)
    call(op, *(@arguments + args), &blk)
  end

  # Returns the passthru arguments.

  def to_a    ; @arguments ; end

  # Returns the underlying procedure.

  def to_proc ; @procedure ; end
end


#--
#module Kernel
#
#  def dispatch( *targets, &dispatch )
#    Dispatch.new( *targets, &dispatch )
#  end
#
#end
#++



#  _____         _
# |_   _|__  ___| |_
#   | |/ _ \/ __| __|
#   | |  __/\__ \ |_
#   |_|\___||___/\__|
#

=begin testing

  require 'test/unit'

  class TC_Functor < Test::Unit::TestCase

    def test_function
      f = Functor.new { |op, x| x.send(op, x) }
      assert_equal( 2, f + 1 ) #=> 2
      assert_equal( 4, f + 2 ) #=> 4
      assert_equal( 6, f + 3 ) #=> 6
      assert_equal( 1, f * 1 ) #=> 1
      assert_equal( 4, f * 2 ) #=> 4
      assert_equal( 9, f * 3 ) #=> 9
    end

    def test_decoration
      f = Functor.new("A") { |op, a, x| x.send(op, a + x) }
      assert_equal( 'BAB', f + 'B' )
      assert_equal( 'CAC', f + 'C' )
      assert_equal( 'DAD', f + 'D' )
    end

  end

=end

