perl-diddler has asked for the
wisdom of the Perl Monks concerning the following question:

I have a module I created .. and realized I wanted to change something in the interface.
So... I've been 'versioning' each module change, and would bump the version in the new module.. BUT, how could my module detect -- when a user says: "use module 1.0.0", and I'm working in module "2.0.0"? My 2.0.0 module would never get called as far as I can tell...no?

What I would want to do is have my 2.0.0 module detect if it was called by something prior to 2.0, and apply the old interface semantics for compatibility.

The only way I can think of is to NOT version my code, but expect that any "using modules" will use "some version" (which? .. How would they know if I don't put a version in my code?) but then if I was called as a versionless module, I think I might receive the version number as a parameter, and could then implement the behavior I want -- but it seems that to do that I have to NOT use a version number -- which would sorta make it hard for any new code
to know what version to specify to get behavior "X"...

when a user says: "use module 1.0.0", and I'm working in module "2.0.0"? My 2.0.0 module would never get called as far as I can tell...no?

What have you tried so far? What evidence do you have that supports your assumption?

The easy way to do that is to separate "module version" and "API version", and have the use line pass the API version as parameter. I recommend making the main module just a stub module and having "::API*" modules as submodules that get loaded resp. instantiated depending on what the user wants.

For example, a module could implement API1 and API2 in the following structure:

I don't want to use multiple files for the same module -- duplicating code is a bad practice.

One of the reasons I tend to put many modules in 1 file during development, is to find places to look for common code to apply reductions to. Only after some package has sat in my file doing nothing useful (other than providing it's function) other than than taking up useful space in my file.. I consider splitting it off... I.e. it's so far out of my head space, because I'm just "using the code", that it's not changing, and it's presence in the file is now a bother ... time to split it off and take it as a library component...OR...that might be too much of a bother...until I'm working on another program that wants to use the module...that's when I really work on splitting things...

But I would think I had too many routines shared between the versions. I was only thinking of forcing a different parameter order on 1 routine -- would you really think it a good idea to duplicate hundreds of lines just to change a few?

Maybe you want to reuse parts of ::API1 in ::API2, then? It's only you who claims that you have to duplicate code between the modules. Maybe having a preconfigured API "object" which contains the default parameters would be an option too, if the "API" only consists of default parameters.

This has nothing to do with putting many modules in one file. You've already been advised against that, but this discussion is far besides the point.

You don't have to put different packages in different files, only when you need to make them available to use from other files. SOAP::Lite comes to mind as a module where it exhibits much different behavior depending on what's needed (eg, do we use 'xsi:null' or 'xsi:nil' for this version of XML Schema?

SOAP::Lite might be a bit of an extreme example, as they pack a *lot* into one file. (I count 28 package declarations currently, and it looks like more might be dynamically generated; I remember bugging BareBones Software for *years* about marking packages in automatically generated list of functions so that I could use it to find my way around the file).

Thanks for the tip...I think in all the other stuff about use, I missed that section... Will have to go study it again...(so many places are like that -- especially when functions get updated...)...
Yup, that sounds about perfect... didn't realize version was an overridable universal method...

If this is an OO-based module, you could pass a protocol version to the constructor. (Kinda like many file formats include a version.) If it's not, you could write your own import sub and use use module "1.0.0"; instead of use module 1.0.0;. This would be a bit error prone.

Parallel APIs:

The other approach is add to the interface instead of replacing it. You can use two different module names: "module" for 1.x.x and "module2" for 2.x.x, or you can add methods instead of replacing old ones (do_something2 is the new interface for do_someting).

If this is an OO-based module, you could pass a protocol version to the constructor. (Kinda like many file formats include a version.) If it's not, you could write your own import sub and use use module "1.0.0"; instead of use module 1.0.0;. This would be a bit error prone.

And if you go this route, the first version that handles protocol/API/etc. versions should assume that "no API version given" is a valid input that means "the oldest version that did not know how to handle API versions", for the sake of backwards compatibility.

I wish I'd thought of that in designing the 1st... I thought about passing the version... but the earlier code would still have to be rewritten .... THOUGH, it is SIMILAR to what I was thinking about... and that is adding a new param, that the new stuff looks for but if not there, uses old behavior...(I think someone else suggested similar)...

Still... gonna have to look at use more ... sounds like there may be some version stuff I missed...
Though I'd think I'd go the opposite on the defaults w/o the version number.
I don't want to have to support old stuff forever. So unversioned calls would get the newest behavior. It's assumed that most people want the newest and latest and greatest unless they really ask for the the outdated stuff, I don't see why software should assume the opposite is true.
Imaging if people ran programs that didn't ask for a version on Windows 7 and got Dos 1.0 behavior... :-)....pretty screwed up eh?

In general, it is vastly simpler to have two separate modules. That said, here's an example including test cases for a module which exports three functions (alphabet, colours and numbers) where two of the functions have different behaviours in API 1 and API 2.

The key trick here is using Sub::Exporter to install different coderefs into the caller's package depending on what API version they have requested. Some of the examples even show how a caller can request parts of API 1 and parts of API 2.

I've used an Exporter-type module because that's the most challenging to implement. If you're writing object-oriented code instead, then things are probably a bit easier. You can usually just create two subclasses of a base class.

Not having a version number is a bad idea. It will lead to all kinds of misunderstandings. How can anyone tell you what "version" they have installed if there is no version number?

If you ask me, I'd have no less than 3 modules: one with API1, one with API2 and one with all common code to API1 and API2.

Another solution is, depending on your code, to have the API modules contain only your public interface. Then all "working" code is in the third module. That will make it easier to maintain the whole by separating the concerns "interface" and "working code".

Or the API1 module could contain some routines that "translate" API1 calls into their API2 version and then handover execution to API2 which calls upon its own routines. You could do without a third module in this case.

CountZero

A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

Not an answer to your question, rather something you may want to include. I was told about semantic versioning (http://semver.org/) at a London PM meeting, and have passed it on to a few people, all of whom say it's a fine idea. YMMV.