I find the data structures that you have constructed here barely
understandable:
my %required_by_type = (
int => [qw(min max)],
real => [qw(min max)],
enum => [qw(options)],
);
for my $f (@required_common, @{ $required_by_type{$entry->{type} //
''} // [] }) {
[qw(min max)] is an array inside an array reference? I think? Do we
need two levels of nesting?
I think this // notation is unnecessarily confusing, and why do we need
two of them. I thought your first patch
+ bool => [], # no extra required fields
+ string => [], # no extra required fields
was clearer. And that way, we also check that the field type is one of
the ones we support.