# Partial functions # Implemented by Emil Lundström # Requires Higher order messaging require 'hom.rb' # Given an object and a method id (a symbol), returns the least # number of arguments that method needs. def get_least_args(obj, meth) begin arty = obj.method(meth).arity rescue arty = 0 end arty < 0 ? -(arty+1) : arty end # Higher order message. partial returns an object that allows # any method to be called on it. However, when a call is made, # rather than being called, it's stored in a new HOM object. # The methods stacked can only be executed by explicitly # calling the :call method on the object returned. # # Examples: # # $ f = partial.puts("hello!") # $ f.call # "hello!" # => nil # # $ g = partial.puts # $ g.call("hello again!") # "hello again!" # => nil # # $ h = [1,2,3].partial.-.do_map.+ # $ h.call([2],10) # => [11,13] # # Note that the parameters passed to call are distributed to the # methods appropriately, using the information Method#arity # provides. However, when arity returns a negative value (implying # that there's a variable number of arguments), only the required # arguments are passed. Any extra arguments are used for the final # method. public def partial(calls=[]) HigherOrderMessage.new do |id, *args| if id == :call # User called :call: run all stored messages calls.inject(self) do |n, i| if i.equal?(calls.last) # If this is the last method call, pass all remaining # arguments to it. sargs = i[1] + args else # Try to figure out how many arguments to give this # method call. nargs = get_least_args(n,i[0]) - i[1].length nargs = 0 if nargs < 0 sargs = i[1] + args[0,nargs] args = args[nargs..-1] end n.__send__(i[0],*sargs) end else # Message id not :call, so just store the call arguments. partial(calls.dup << [id, args]) end end end