Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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
You can’t perform that action at this time.