We use Perl Moo.
Let there is defined a set of attributes:
use Types::Standard qw(Str Int Num Maybe);
has 'x' => (is=>'rw', isa=>Str);
has 'y' => (is=>'rw', isa=>Int);
has 'z' => (is=>'rw', isa=>Int);
# here to insert make_optional() described below
Moo does not have a Meta Object Protocol. Without it, you cannot go back and alter stuff.
There is no meta object. If you need this level of complexity you need Moose - Moo is small because it explicitly does not provide a metaprotocol.
Furthermore, the types are just code refs.
There is no built-in type system. isa is verified with a coderef; if you need complex types, Type::Tiny can provide types, type libraries, and will work seamlessly with both Moo and Moose.
What you could do is maybe write a type that access some kind of singleton somewhere else to decide if it behaves like
Str, but that's a long shot and probably ugly and crazy and you shouldn't do it.