Defensive programmers anticipate what might go wrong. Robust code handles the unexpected, partly by minimizing the surface area of potential problems. The fewer things that can go wrong, the fewer things that will go wrong. (Things will still go wrong, but you can write safer code if you're clever.)
Yuval Kogman asked Are we ready to ditch string errors? I am; there's a general principle of API design beyond his question.
One problem with
die "Some error!" is how to identify what error that represents—not to a programmer or user, who ostensibly speaks enough English and problem domain jargon to have some idea of what the error means—but the rest of the program. How does your code catch this error and distinguish it from some other type of error? Can you determine which of the two you can handle and which you must delegate?
split or the regular expression engine and prepare to write heuristics which guess, and woe to you if someone someday internationalizes your error messages or runs all of your exceptions through a logging mechanism which changes their formatting slightly or....
The problem is that you can't take advantage of the structure of the exception data because it's not present in the string. The same goes for DBI's connection strings:
my $dbh = DBI->connect( 'dbi:DriverName:database=database_name;host=hostname;port=port' );
As the documentation suggests in the very next sentence:
There is no standard for the text following the driver name. Each driver is free to use whatever syntax it wants.
Compare this to a keyword argument form:
>my $dbh = DBI->connect( driver => 'DriverName', database => 'database_name', host => 'hostname', port => 'port', extra => 'arguments', );
This has several advantages. The method doesn't have to guess (or parse) the string. The layout and vertical alignment makes the keyword form easier to read and to modify. DBDs can decorate and augment this argument list without parsing and recreating a string. Verification and default arguments are much easier.
The same argument goes for using a module such as File::stat instead of parsing the output of
`ls -l filename`.
The same argument goes for... you get the point. It's far too easy to unfold the regex widget from the swiss-army chainsaw when a little bit of caution decomposing data into structured data makes your programs safer, easier to use, more flexible, and more robust.
(I consider sometimes how a language would look if it had only keyword arguments and how you could optimize them with immutable, internable strings and cached call sites and a zero-copy register allocation mechanism, but I made it as far as writing a self-hosting garbage collector before I had real work to do.)