sub a_subroutine { my ($opt, @bad ) = @_; my $string = delete $opt->{a_descriptively_named_string}; my $desktop_info = delete $opt->{desktop_config}; my $important_files = delete $opt->(important_files} // []; checkopt( \%opt, \@bad, a_descriptively_named_string => defined $string, desktop_config => ref $desktop_info eq 'HASH', important_files => ref $important_files eq 'ARRAY', ); # Do stuff. } # checkopt is a nice little function along these line. # You can customize it a bit, (add support for testing for invocants, fancy crap with passing additional error messages, what have you) # But in pretty much this form it has caught made many a bug shallow sub checkopt { while (@_) { if ( ref eq 'HASH' ) { my $opts = shift; @args = keys @$opts; croak "Unexpected options in subroutine call: @args" if %$opts; } elsif ( ref eq 'ARRAY' ) { my $bad = shift; croak "Unexpected arguments in subroutine call." if @$bad } else { my $option_name = shift; my $option_okay = shift; croak "Invalid option '$option_name' in subroutine call" unless $option_okay; } } }