On Tue, 2004-02-24 at 01:38, David Manura wrote:
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().
BEGIN{
my $err1;
eval 'use Text::Balanced 1.96';
$@ and do {
$err1 = $@;
eval 'use Text::Balanced 1.95';
$@ and die <<EOERR;
tried to use Text::Balanced 1.96 and got
$err1
tried to use Text::Balanced 1.95 and got $@
EOERR } }
This is less of a hack, I admit, although more verbose than I would like.
Would this solution hold in general when breaking module interfaces?
Yep, but it's ugly as hell.
It could be slightly shortened to this:
use Text::Balanced 1.95_1.96; # ok
I also propose an important difference in how this is interpreted. The number '1.95_1.96' as a whole may instead be understood *conventionally*. It is as a single version number, although more specifically a 'pseudo'-version number. It is 'pseudo' because it represents a version with a well-defined interface but with no implementation (i.e. no implementation of the module has a $VERSION variable set equal to this number). If this version of the module were to exist, though, its interface would consist, loosely speaking, of the 1.95 interface minus the parts incompatible with 1.96 (think: set difference).
So, we can say that we want the following to be true:
- Code asserted to be compatible with 1.96 may be incompatible with 1.95. (We don't care whether it is compatible with 1.95_1.96 since 1.95_1.96 is not implemented and hence cannot be installed.) - Code asserted to be compatible with 1.95_1.96 is compatible with both 1.95 and 1.96. - Code asserted compatible with 1.95 may be incompatible with 1.96.
and also
- Code asserted to be compatible with 1.97 may be incompatible with previous versions. - Code asserted to be compatible with 1.95_1.97 is compatible with 1.95 and 1.97 but may be incompatible with 1.96.
So, does this work in practice?
If you have Text::Balanced 1.95 installed, then "use Text::Balanced 1.95_1.96" will succeed by accident. It is the caller's responsibility to ensure that one's code does not violate the contract of 1.95 or 1.96. So, it might not be a compile- or runtime error to do a "use Text::Balanced 1.95_1.96" and then take advantage of the entire 1.95 interface--just don't do that. "use Text::Balanced 1.95_1.97" is analogous.
If, however, 1.96 is installed, "use Text::Balanced 1.95_1.96" will work by design. The import() function of 1.96 could be overridden to warn if 1.95 or no version is specified. However, no warning will be generated if 1.95_1.96 is specified. "use Text::Balanced 1.95_1.97" will warn, however, since 1.96 knows that the interface was broken at 1.96, and no compatible version is specified.
I'm somewhat repeating myself, but the new idea is that '1.95_1.96' is now understood as a single version number.
As a sidenote, say, hypothetically, that an implementation for '1.95_1.96' did exist, you installed it, and then you had some old code that did
use Text::Balanced 1.95;
Your code is asserted to be compatible with 1.95, and Perl gracefully loads 1.95_1.96, which does not implement the entire 1.95 functionality. Therefore, to ensure correctness, we would need to add code inside 1.95_1.96 to trigger a warning in this case. No problem there. In fact, as before, the responsibility for version consistency checking is shared between Exporter.pm and one's module.
Some of these concepts are resembling those in the "VERSION as (interface,revision) pair and CPAN++" discussion I recently had on this list, where a versioning scheme was suggested in which versions would be of the form x.y such that x.y is compatible with u.v iff x=u and y>=v. Here, however, version numbers themselves imply no version compatibility relation.
Enough yapping. Time to implement.
In the situation where you want to have two different behaviors from the same-named module, different versions, used in different packages in the same program, a way to do that might be to choose a named package variable (such as ${caller()."::Text::Balanced::Version"} ) and assign to it within the VERSION subroutine
${caller()."::Text::Balanced::Version"} = $req_ver
then when there's a choice to zag or zig, that's where you go for a hint. (I have verified that caller() is available within &VERSION.)
(This second proposal is of course independent of the proposal above, which does not attempt to run two interfaces simultaneously, not counting pseudo-interfaces.)
Overriding VERSION is what I originally tried, but it didn't reliably set caller(). For example,
=============== Test1.pm ================ package Test1; use Exporter; use base qw(Exporter);
our $VERSION = '2.00'; our @EXPORT_OK = qw(&test1);
sub test1 { }
sub VERSION { print "caller=" . caller(1) . "]\n"; }
1;
============= test.pl ====================
package main;
use Test1 1.00; # outputs 'caller=main' use Test1 qw(1.00); # outputs 'caller=Exporter' use Test1 1.00, qw(test1); # outputs 'caller=Exporter'
===========================================
The workaround is to instead override import().
There still can be unexpected problems in new code such as
package Module1; sub test { $_[0]->(); } # uses old interface
package Module2; use MyModule 2.00; # new interface
Module1::test(\&MyModule::myfunc);
unless one uses a closure to force the call to be executed within the current package context:
Module1::test(sub { MyModule::myfunc(@_) }); # new interface
-davidm