Skip to content

Instantly share code, notes, and snippets.

@roman
Created December 27, 2010 04:35
Show Gist options
  • Save roman/755865 to your computer and use it in GitHub Desktop.
Save roman/755865 to your computer and use it in GitHub Desktop.
curry.rb
class Proc
alias_method :old_call, :call
public
def curried?
!!@curried
end
def curry(level = nil)
CurriedProcHelper.new(self).curry(level)
end
def call(*args)
if self.curried?
args.inject(self) do |last_proc, a|
last_proc = last_proc.old_call(a)
last_proc
end
else
old_call(*args)
end
end
private
def currify!
@curried = true
self
end
class CurriedProcHelper
def initialize(block)
@proc = block
end
def curry(level = nil)
level ||= number_of_proc_parameters
return @proc unless it_has_parameters_to_curry?
@proc.instance_eval(append_proc_tail(append_proc_call(build_proc_head(level)), level))
end
private
def build_proc_head(level)
proc_body = ""
l = 'a'
number_of_proc_parameters.times do |index|
if level < index + 1
proc_body << "proc { |#{parameter_name_list(l)}| "
break
else
proc_body << "proc { |#{l}| "
end
l.next!
end
proc_body
end
def append_proc_call(proc_body)
proc_body << "self.call(#{parameter_name_list})"
proc_body
end
def append_proc_tail(proc_body, level)
number_of_proc_parameters.times do |index|
break if level < index
proc_body << " }"
proc_body << ".send(:currify!)" unless index == 0
end
proc_body
end
def parameter_name_list(from = 'a')
(from ... last_parameter_letter).to_a.join(', ')
end
def last_parameter_letter
l = 'a'
@_curry_last_paremeter_letter ||= (number_of_proc_parameters.times { l.next! } && l)
end
def it_has_parameters_to_curry?
@proc.arity < -1 || @proc.arity > 0
end
def number_of_proc_parameters
@proc.arity < -1 ? @proc.arity.abs - 1 : @proc.arity
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment