While toying around with enums as boolean options to a routine, i found the default error message less then awesome.

Constraint type check failed for parameter '@options'

It would be hard to be even less specific. Let’s create a few exceptions to tell what is going on when things go wrong.

class X::Paramenter::Exclusive is Exception { has $.type; method message { "Parameters of {$.type.perl} are mutual exclusive" } }

Now we can check if options of Find::Type are exclusive and complain accordingly.

&& ( exclusive-argument(@options, Find::Type) or fail X::Paramenter::Exclusive.new(type => Find::Type) ) class X::Parameter::UnrecognisedOption is Exception { has $.type; has $.unrecognised; method message { "Option { $.unrecognised } not any of { $.type.map({ (.^name ~ '::') xx * Z~ .enums.keys.flat }).flat.join(', ') }" } }

Since enums are containers for types and those got names we can use set operators to check and single out none matching options (basically anything the +@options slurps up we don’t know).

or fail X::Parameter::UnrecognisedOption.new(type => (Find::Type, Find::Options), unrecognised => .item ∖ (|Find::Type::.values, |Find::Options::.values) )

Stitching the error message together is a bit more involved because we can get a list of all enum keys in a given enum but those don’t know their qualified name. We have to prefix with the enum name and :: by hand.

class X::Parameter::UnrecognisedOption is Exception { has $.type; has $.unrecognised; method message { "Option { $.unrecognised } not any of { $.type.map: { (.^name ~ '::') xx * Z~ .enums.keys.flat } }" } }

This results in a much more awesome error message:

Option 42 not any of Type::File, Type::Dir, Type::Symlink, Options::Recursive, Options::Keep-going

This looks all quite regular. We have a slurpy that is kind of parameterised with one or many enums and those enums may have a flag telling if they act like radio buttons. Sounds like this idiom would fit nicely into a module.