You could always use something like what I just threw together below. But like you said, the code becomes repetitive. Here it is: provides both parameter checking and default values.
sub do_something {
my %default = ( # declare all valid parameter
foo => 'foo argument', # names, providing a default
bar => 'bar argument', # value at the same time (set
baz => undef # to undef for no default)
);
my %params = (%default, @_);
for (keys %params) {
warn "unknown parameter '$_' passed to do_something()"
unless exists $default{$_};
}
# now do stuff.
}