I think what I'll do is simply have Text::Balanced 1.96 issue a warning if it is used without a version number or with a version number prior to 1.96. This means that old code using this module, even though they may still run, will now trigger warnings:
use Text::Balanced; # warn use Text::Balanced 1.95; # warn
which they should, until they are upgraded or Text::Balanced is downgraded. To prevent a warning, new code should do
use Text::Balanced 1.96; # ok
As usual, code that specifies a future version will die:
use Text::Balanced 1.97; # die
and "use Text::Balanced 1.96" will die on systems with only 1.95 installed.
Unfortunately, there seems to be no clean and easy way now to assert that one's code is compatible with both 1.95 and 1.96 and to work with both versions. However, this may do:
use Text::Balanced 1.95_and_1.96; # ok
The above is accidentally accepted by 1.95, and 1.96 could interpret it specially upon overriding import().
If for some reason, we need to break Text::Balanced again (say in version 3.14), and the user installs 3.14, then new code would do
use Text::Balanced 3.14; # ok
while old code will then trigger warnings:
use Text::Balanced 1.96; # warn use Text::Balanced 1.95; # warn use Text::Balanced; # warn
To prepare for the future, 1.96 could support a 'use' option:
use Text::Balanced qw(1.96 -and=3.14); # ok
3.14 will no longer trigger a warning here because the author of the client code asserts itself to be compatible with both versions. (3.15 accepts this statement as well by implication.) Should there be some other, maybe simpler, syntax though?
use Text::Balanced qw(1.96 and 3.14); # ok (possible alternate syntax)
To still retain even 1.95 compatibility, one could do the hackier
use Text::Balanced 1.95_and_1.96_and_3.14; # ok
But if one only did
use Text::Balanced 1.95_and_3.14; use Text::Balanced qw(1.95 and 3.14); # alternate syntax?
then 1.96 would generate a warning on this, while 1.95, 3.14, and 3.15 will accept it without complaint.
There seems to be no very good solution, but I claim the above solution maintains correctness (in the past and future) without causing too much irritation. In most cases, the client code would simply do this from now on:
use Text::Balanced 1.96;
Would this solution hold in general when breaking module interfaces?
-davidm
David Manura wrote:
David,
Using a variant of that, we could let the undocumented 1.95 interface result when doing
use Text::Balanced; # or use Text::Balanced 1.95;
so that existing code will not be broken any more than it is. However, the new interface can result when doing
use Text::Balanced 2.00;
Further, both interfaces may be used simultaneously in the same program as such:
use Text::Balanced 2.00, qw(extract_quotelike);
package Two; use Text::Balanced 1.90, qw(extract_quotelike); my $res = (extract_quotelike('asdf'))[0]; print defined($res) ? "[$res]" : '(undef)'; # prints '[]'
package main; my $res = (extract_quotelike('asdf'))[0]; print defined($res) ? "[$res]" : '(undef)'; # prints '(undef)'
Here is the basic solution:
==========Text/Balanced.pm===========
use vars qw { $VERSION @ISA %EXPORT_TAGS %my_vers $caller}; $VERSION = '2.00'; @ISA = qw ( Exporter );
sub import { foreach (@_) { $my_vers{caller()} = $1 if(/^(\d+); } Text::Balanced->export_to_level(1, @_); }
sub VERSION { my $req_ver = $_[1]; $req_ver < 1.87 and croak "Text::Balanced $VERSION is incompatible with the requested " . "version $req_ver."; $req_ver > $VERSION and croak "Text::Balanced $VERSION is older than the requested " . "version $req_ver."; return $req_ver; }
sub _fail { ... return (undef,$$textref) if $my_vers{$caller} == 2; # new style return ('',$$textref,''); # old style (v. 1.xx) ... }
sub extract_quotelike (;$$) { ... $caller = caller() unless caller() eq 'Text::Balanced'; ... return _fail($wantarray, $textref) unless @match; ... };
sub extract_multiple (;$$$$) { $caller = caller() unless caller() eq 'Text::Balanced'; ... # this can call extract_quotelike() ... }
===========================
The complexity with using caller() is needed unfortunately since the module has a functional interface. I'll think this solution over some more since I have doubts that the added correctness with existing code is worth making new code potentially more brittle.
A simpler solution would be to just croak if a 1.95 or prior version is specified in "use" and possibly carp or croak (probably carp) if *no version* is specified. That comes with its own small issues of course.
-davidm
david wrote:
Vagn Johansen wrote:
How are interface changes handled on CPAN?
They're not. I'm trying to promote a pradigm of including a VERSION subroutine that
will croak (or at least die) when you ask for a non-forwards-compat. version.
In theory, you change the name when you change the itnerface, instead of merely
updating the version number.
How do you avoid breaking old programs when the interface changes?Issue a new module name with the new interface, and maintain both versions for bugs.