A little spike on implementing currying for keyword arguments after reading: https://bugs.ruby-lang.org/issues/16113
# frozen_string_literal: true | |
module KeywordCurry | |
refine Method do | |
def curry(curry_arity = nil) | |
to_proc.curry(curry_arity) | |
end | |
end | |
refine Proc do | |
def curry(curry_arity = nil) | |
params = parameters | |
min_args = params.count { |type, _| type == :req } | |
if curry_arity && lambda? | |
unless arity.negative? && arity != curry_arity | |
raise ArgumentError, "Wrong number of arguments (#{curry_arity} for #{arity})" | |
end | |
max_args = params.count { |type, _| type != :block } | |
no_rest = params.none? { |type, _| type == :rest } | |
if curry_arity < min_args || no_rest && curry_arity > max_args | |
additional = no_rest ? "..#{max_args}" : '+' | |
raise ArgumentError, 'Wrong number of arguments ' \ | |
"(given #{curry_arity}, expected #{min_args}#{additional})" | |
end | |
end | |
req_kwargs = params.filter_map { |type, name| name if type == :keyreq } | |
-> *args, **kwargs do | |
currying_done = !curry_arity&.-(args.size)&.nonzero? | |
args_met = args.size >= min_args | |
kwargs_met = req_kwargs.all? { |req_kwarg| kwargs.key? req_kwarg } | |
if currying_done && args_met && kwargs_met | |
call(*args + kwargs.map { |key, value| {key => value} }) | |
else | |
-> *curry_args, **curry_kwargs do | |
curry(curry_arity).call(*args, *curry_args, **kwargs, **curry_kwargs) | |
end | |
end | |
end | |
end | |
end | |
end | |
require 'json' | |
require 'minitest/autorun' | |
require 'minitest/pride' | |
describe KeywordCurry do | |
using KeywordCurry | |
it 'curries keyword arguments' do | |
assert_equal JSON.:parse.curry.call(symbolize_names: true).call('{"aim":true}'), aim: true | |
end | |
it 'accepts a curried arity' do | |
h = {first: {second: {third: 42}}} | |
assert_equal h.:dig.curry(3).call(:first).call(:second).call(:third), 42 | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment