Re: VERSION as (interface,revision) pair and CPAN++
On Wednesday, January 28, 2004, at 05:34 am, David Manura wrote: [snip] Yes. However, the added benefit of Version::Split is that it moves the maintanance of '= 1.2, != 1.5, 2.0' away from Foo::Bar and centrally into Other::Module. As Fergal stated, V::S tries to move the version problem from the user (who in general knows the least about what versions are compatible) to the author (who in theory knows most). This should be less error prone and easier to maintain. [snip] Hmmm... I'm not so sure that that's always (or even mostly) true. - I've certainly been caught by many accidentally introduced bugs / subtle interface changes that the module author wasn't aware of (so no interface change) - I've also carried on using modules that have had interface changes without problems - because I've not been using that particular piece of the API. Just a thought... Adrian
Re: VERSION as (interface,revision) pair and CPAN++
Hi Fergal and other, The difference is that Module::Build forces the Foo::Bar's author to work out what current versions of Some::Module and Other::Module are suitable and to try to predict what future version will still be compatible. This is time consuming and error prone (predicting the future isn't easy) and it has to ... What I meant is that we shouldn't have two ways (and 2 places) of telling what we need for our modules to work. Other have pointed some problems with your scheme so I won't repeat them here. I understand what you want to achieve and I think it's good but please keep it in one place. Can't you coordinated your efforts with Module::Build so # old example my $build = Module::Build-new ( module_name = 'Foo::Bar', license = 'perl', requires = { 'perl' = '5.6.1', 'Some::Module' = '1.23', 'Other::Module' = '= 1.2, != 1.5, 2.0', }, ); ... requires = { 'perl' = '5.6.1', 'Some::Module' = 'COMPATIBLE_WITH 1.23', # or the like 'Other::Module' = '= 1.2, != 1.5, 2.0', }, Instead for drawing in a new module that most _won't_ use, you make it in the main stream new installer. IMO, Module::Build has some problems (which I am sure will be fixed soon) and your proposed module also have some problems. It would be nice if the versionning /installation questions where handled in a coordinated way. Module devlopers are lazy snakes, they want to sun bath on one stone only! Cheers, Nadim.
Re: VERSION as (interface,revision) pair and CPAN++
On Fri, Jan 30, 2004 at 03:02:53PM +0100, khemir nadim wrote: What I meant is that we shouldn't have two ways (and 2 places) of telling what we need for our modules to work. I agree, there should be only one place where Some::Module's compatibility information is declared, whether that's in Some::Module's Build.PL or in Foo/Bar.pm, is not really important, what's important is that Some::Module's developer has to figure out the details, not Some::Module's user. Other have pointed some problems with your scheme so I won't repeat them here. I understand what you want to achieve and I think it's good but please keep it in one place. Can't you coordinated your efforts with Module::Build so # old example my $build = Module::Build-new ( module_name = 'Foo::Bar', license = 'perl', requires = { 'perl' = '5.6.1', 'Some::Module' = '1.23', 'Other::Module' = '= 1.2, != 1.5, 2.0', }, ); ... requires = { 'perl' = '5.6.1', 'Some::Module' = 'COMPATIBLE_WITH 1.23', # or the like 'Other::Module' = '= 1.2, != 1.5, 2.0', }, There should be no need for COMPATIBLE_WITH. 'Some::Module' = '1.23' should work fine with any whacko versioning scheme that anyone ever came up with if Module::Build is behaving correctly. Behaving correctly means letting Some::Module-VERSION decide what is compatible and what is no. This is the officially documented way to do it. MakeMaker almost does this. From a quick look at the source, I think Module::Build definitely doesn't. Instead, it attempts to find the version by snooping around in the .pm file. So it basically ignores any custom VERSION method. It should be fairly easy to fix that in both cases. Instead for drawing in a new module that most _won't_ use, you make it in the main stream new installer. The new module is designed to make it easy for people to have a custom VERSION method that does something better than the current default. However, depending on this new module is obviously a problem - extra dependencies are no fun for anyone. The next version should help solve that, F
Re: VERSION as (interface,revision) pair and CPAN++
Hi Nadim, The difference is that Module::Build forces the Foo::Bar's author to work out what current versions of Some::Module and Other::Module are suitable and to try to predict what future version will still be compatible. This is time consuming and error prone (predicting the future isn't easy) and it has to be done for every module that requires these other modules. In fact I think most module authors do not test these things thoroughly - I know I don't, it's just too much of a pain. If Some::Module and Other::Module used Version::Split for their version information then Foo::Bar's author could just say well I developed it with Some::Module 1.23 and Other::Module 1.2 so only accecpt a version that is declared to be compatible with those. That way all the work on building the compatibility information is only done once and it's done by Some::Module and Other::Module's authors, which is good because they're the people who should know most about their own modules. Foo::Bar's author never has to change his requires just because Other::Module 1.9 has been released and works in a different way. You also get the interesting side effect that if Foo::Bar's tests all pass when using Some::Module 1.23 and they fail with 1.24 (which has been declared to be comapatible) then both Foo::Bar's and Some::Module's authors can be informed about it and try to work out who has the bug, F On Tue, Jan 27, 2004 at 08:54:47AM +0100, khemir nadim wrote: Hmm, isn't that what Module::Build is already offering to you? use Module::Build; my $build = Module::Build-new ( module_name = 'Foo::Bar', license = 'perl', requires = { 'perl' = '5.6.1', 'Some::Module' = '1.23', 'Other::Module' = '= 1.2, != 1.5, 2.0', }, ); $build-create_build_script;My 2 cents, Nadim.
Re: VERSION as (interface,revision) pair and CPAN++
Yes it's confusing, I'm having trouble following bits of it, I'm sure anyone else who's actually bothering is too. Hopefully all the confusion will be gone at the end and only clarity will remain, that or utter confusion - it could end up either way really. To see why the current situation is most definitely broken, take the example of Parse::RecDescent again. 1.90 changed in a fundamental way. Using the current system, what should the author have done? Calling it 2.0 would be no good because use Parse::RecDescent 1.84; works fine with 2.0 and CPAN.pm would download 2.0 if you told it you need at least 1.84. The correct thing to do was to release Parse::RecDescent2 v1.0 which means that CPAN should be cluttered up with copies of modules with numbers on the end including perhaps Net::POP32 which might be the 2nd version of Net::POP3 or it might be the 32nd of Net::POP or it might be an implementation of some future POP32 protocol. In a serious production environment you should be doing exactly what you do but when you want to try out some cool looking module, you shouldn't have to worry about with the entire revision history of all it's dependencies and all their dependencies and so on, it should just work or fail at compile time, F On Wed, Jan 28, 2004 at 11:08:23PM -0500, Lincoln A. Baxter wrote: Phew... Only one comment: KISS (Keep It Simple Stupid) This is WAY too confusing! No one will be able to figure it out, or want to. What we have now is not really that broken, especially if one regression tests his applications when new versions of modules are installed. Actually, we build our offically supported perl tree which we deploy to all of our boxes, and all of our applications use. And when we upgrade things, we build a whole new tree, which we regression test every application with before we roll it into production. No fancy versioning emnumeration scheme can replace this testing, and what we have now works well enough (I think). Most module authors I think are pretty good about documenting what they change in the Changes file. Lincoln On Wed, 2004-01-28 at 00:28, David Manura wrote: Fergal Daly wrote: On Saturday 24 January 2004 18:27, David Manura wrote: (1) All code that works with Version A will also work with subsequent Version B. (e.g. adding new functions) (2) There exists code that works with Version A but will not work with Version B. (e.g. changing existing function signatures) (3) There exists code that works with Version A, will not work with Version B, but will work with an even more future Version C. (probably a rare case) To handle #1 and #2, we could require all interface version numbers be of the form x.y such for any two increasing interface numbers x.y and u.v, assertion #1 is true iff x=u and v=y. Assertion #2 is true iff the opposite is true (i.e. x!=u or vy). There is no use for long version numbers as mentioned (e.g. 1.2.3.4). I think this might make more sense alright and I'll probably change V::S to work like that. However I don't agree with having no use for longer version numbers. For a start, people do use them and I don't want to cut out something people use. Also, when you have 1.2 and you want to experiment with a new addition but you're not sure if you have it right you can release 1.2.1.1 which is implicitly compatible with 1.2 . If you then think of a better interface you can release 1.2.2.1 which would still be compatible with 1.2 but would have no implied relation to 1.2.1.1. You can keep releasing 1.2.x.y until you get to say 1.2.6.1 at which point you're happy. Then you can rerelease that as 1.3 and declare it compatible with 1.2.6.1 . This let you have development tracks without having to including lots of explicit compatibility relations. Branching and backtracking is an essential part of exploring so supporting it without any effort for the author is good. So to rephrase, B implements the interface of A (say B = A where = is like implies in maths) if ( version_head(A) == version_head(B) and version_tail(A) version_tail(B) ) or ( version(B) begins with version(A) ) where version_head means all except the last number and version_tail means the last number So 1.2 = 1.1, 1.2.1 = 1.2, 1.2.2 = 1.2.1 2.1 not = 1.1 but you could declare it to be true. 1.2.2.1 = 1.2 but 1.2.2.1 not = 1.2.1.1 and = is a transitive relation, just like implies in maths, so they can be chained together. 1.2.1 = 1.2 and 1.2 = 1.1 means 1.2.1 = 1.1. So an extension causes an increase and a branch which can be abandonned requires adding 2 more numbers. Actually this is exactly the same as CVS and presumably for the same reason. I'm not sure branching maps cleanly onto the interface versioning scheme as shown above. Let's say you have
Re: VERSION as (interface,revision) pair and CPAN++
On Thursday, January 29, 2004, at 04:08 am, Lincoln A. Baxter wrote: Phew... Only one comment: KISS (Keep It Simple Stupid) [snip] No fancy versioning emnumeration scheme can replace this testing, and what we have now works well enough (I think). Most module authors I think are pretty good about documenting what they change in the Changes file. Amen to that :-) Adrian
Re: VERSION as (interface,revision) pair and CPAN++
Phew... Only one comment: KISS (Keep It Simple Stupid) This is WAY too confusing! No one will be able to figure it out, or want to. What we have now is not really that broken, especially if one regression tests his applications when new versions of modules are installed. Actually, we build our offically supported perl tree which we deploy to all of our boxes, and all of our applications use. And when we upgrade things, we build a whole new tree, which we regression test every application with before we roll it into production. No fancy versioning emnumeration scheme can replace this testing, and what we have now works well enough (I think). Most module authors I think are pretty good about documenting what they change in the Changes file. Lincoln On Wed, 2004-01-28 at 00:28, David Manura wrote: Fergal Daly wrote: On Saturday 24 January 2004 18:27, David Manura wrote: (1) All code that works with Version A will also work with subsequent Version B. (e.g. adding new functions) (2) There exists code that works with Version A but will not work with Version B. (e.g. changing existing function signatures) (3) There exists code that works with Version A, will not work with Version B, but will work with an even more future Version C. (probably a rare case) To handle #1 and #2, we could require all interface version numbers be of the form x.y such for any two increasing interface numbers x.y and u.v, assertion #1 is true iff x=u and v=y. Assertion #2 is true iff the opposite is true (i.e. x!=u or vy). There is no use for long version numbers as mentioned (e.g. 1.2.3.4). I think this might make more sense alright and I'll probably change V::S to work like that. However I don't agree with having no use for longer version numbers. For a start, people do use them and I don't want to cut out something people use. Also, when you have 1.2 and you want to experiment with a new addition but you're not sure if you have it right you can release 1.2.1.1 which is implicitly compatible with 1.2 . If you then think of a better interface you can release 1.2.2.1 which would still be compatible with 1.2 but would have no implied relation to 1.2.1.1. You can keep releasing 1.2.x.y until you get to say 1.2.6.1 at which point you're happy. Then you can rerelease that as 1.3 and declare it compatible with 1.2.6.1 . This let you have development tracks without having to including lots of explicit compatibility relations. Branching and backtracking is an essential part of exploring so supporting it without any effort for the author is good. So to rephrase, B implements the interface of A (say B = A where = is like implies in maths) if ( version_head(A) == version_head(B) and version_tail(A) version_tail(B) ) or ( version(B) begins with version(A) ) where version_head means all except the last number and version_tail means the last number So 1.2 = 1.1, 1.2.1 = 1.2, 1.2.2 = 1.2.1 2.1 not = 1.1 but you could declare it to be true. 1.2.2.1 = 1.2 but 1.2.2.1 not = 1.2.1.1 and = is a transitive relation, just like implies in maths, so they can be chained together. 1.2.1 = 1.2 and 1.2 = 1.1 means 1.2.1 = 1.1. So an extension causes an increase and a branch which can be abandonned requires adding 2 more numbers. Actually this is exactly the same as CVS and presumably for the same reason. I'm not sure branching maps cleanly onto the interface versioning scheme as shown above. Let's say you have 1.2. You then branch to 1.2.1.1 = 1.2. Meanwhile, in your main trunk, you create 1.3 = 1.2. OK, now back in the branch, say you want to introduce an incompatible change to 1.2.1.1. There are actually two ways in which your change can be incompatible: with respect to 1.2.1.1 only or with respect to 1.2. You provided an example of the first case, where we introduce 1.2.2.1 =/= 1.2.2.1 yet 1.2.2.1 = 1.2. However, what shall we do if we need to introduce a change incompatible with 1.2? Number it 1.3? We can't do that because 1.3 has already been assigned in the main trunk. Maybe the branch numbers should be of the form x.y.b.u.v where x.y is the main trunk revision, b is the branch number, and u.v is the branch revision. For simplicity, we'll also eliminate the distinction between changes that are incompatible only with the current branch revision and changes that are incompatible with the main trunk revision. The scheme for x.y will be exactly the same as, yet independent of, the scheme for u.v. So, the following relations are implicit: 1.2.1.1.1 === 1.2 1.2.1.1.2 === 1.2.1.1 1.2.1.2.1 =/= 1.2 (note!) 1.2.1.2.2 === 1.2.1.2.1 1.2.2.1.1 === 1.2 (a second branch) 1.2.2.1.2 === 1.2.2.1.1 1.2.2.2.1 =/= 1.2.2.1.2 1.3 =/= 1.2.b.u.v for all b, u, v 1.3 === 1.2 This seems workable, but
Re: VERSION as (interface,revision) pair and CPAN++
On Saturday 24 January 2004 18:27, David Manura wrote: (1) All code that works with Version A will also work with subsequent Version B. (e.g. adding new functions) (2) There exists code that works with Version A but will not work with Version B. (e.g. changing existing function signatures) (3) There exists code that works with Version A, will not work with Version B, but will work with an even more future Version C. (probably a rare case) To handle #1 and #2, we could require all interface version numbers be of the form x.y such for any two increasing interface numbers x.y and u.v, assertion #1 is true iff x=u and v=y. Assertion #2 is true iff the opposite is true (i.e. x!=u or vy). There is no use for long version numbers as mentioned (e.g. 1.2.3.4). I think this might make more sense alright and I'll probably change V::S to work like that. However I don't agree with having no use for longer version numbers. For a start, people do use them and I don't want to cut out something people use. Also, when you have 1.2 and you want to experiment with a new addition but you're not sure if you have it right you can release 1.2.1.1 which is implicitly compatible with 1.2 . If you then think of a better interface you can release 1.2.2.1 which would still be compatible with 1.2 but would have no implied relation to 1.2.1.1. You can keep releasing 1.2.x.y until you get to say 1.2.6.1 at which point you're happy. Then you can rerelease that as 1.3 and declare it compatible with 1.2.6.1 . This let you have development tracks without having to including lots of explicit compatibility relations. Branching and backtracking is an essential part of exploring so supporting it without any effort for the author is good. So to rephrase, B implements the interface of A (say B = A where = is like implies in maths) if ( version_head(A) == version_head(B) and version_tail(A) version_tail(B) ) or ( version(B) begins with version(A) ) where version_head means all except the last number and version_tail means the last number So 1.2 = 1.1, 1.2.1 = 1.2, 1.2.2 = 1.2.1 2.1 not = 1.1 but you could declare it to be true. 1.2.2.1 = 1.2 but 1.2.2.1 not = 1.2.1.1 and = is a transitive relation, just like implies in maths, so they can be chained together. 1.2.1 = 1.2 and 1.2 = 1.1 means 1.2.1 = 1.1. So an extension causes an increase and a branch which can be abandonned requires adding 2 more numbers. Actually this is exactly the same as CVS and presumably for the same reason. To handle #3, which is more rare under this new proposal, the module probably will need to provide a compatibility map as suggested: use Version::Split qw( 2.1 = 1.1 ); That is, code compatible with 1.1 is compatible with 2.1 but might not be compatible with 2.0 such as if 2.0 removed a function present in 1.1 only for it to appear in 2.1. Furthermore, code compatible with 1.2 may or may not be compatible with 2.1. The above use statement would consider them to be incompatible, but how would we express compatibility if they are actually compatible? Could we do this? use Version::Split qw( 2.1 = 1.2 ); Now, code compatible with 1.2 is known to be compatible with 2.1. Code compatible with 1.1 (or 1.0) is implicitly known to be compatible with 1.2, which in turn is known to be compatible with 2.1. Code known to be compatible only with 1.3, however, remains considered incompatible with 2.1. The above does not suggest that code compatible with 2.1 is compatible with 1.2, rather the reverse. Yes. We declare 2.1 = 1.2 and we know 1.2 = 1.1 so we get 2.1 = 1.1 and 1.0 but we can prove nothing about 2.1 = 1.3, it could be true or false and we're assuming that if we can't prove we don't want it. Are you saying that having split our current version number into 2 parts, I should have actually split it into 3? One to indicate the interface, one to indicate the revision and one to indicate how much code changed? I questioned combining the interface version and amount-of-code-change version into one number. However, could we combine the bug-fix-number and amount-of-code-change number? Are these really different? A major internal refactoring could be fixing bugs even if we never discover them. It could be adding new bugs as well, but bug fixes can also inadvertently introduce new bugs. I propose these two be combined, such as maybe x.y_n, where x.y is the refactoring part and n is the bug fix, or maybe just x.y.z to eliminate the distinction all-together. Given a combined refactoring+bugfix number, does the number hold any significance? You would expect 1.2.15 to be more stable that 1.2.14 as it is probably fixed a bug. Alternately, it might have made a small change to an algorithm--i.e. refactoring. We don't know. We would also expect 2.0.1 to be better implemented/designed that 1.2.14, as the 2.x effort probably did some
Re: VERSION as (interface,revision) pair and CPAN++
* david nicol [EMAIL PROTECTED] [2004-01-22 03:43]: and rewriting Crequire in some way to respect that? Obviously you don't even need to; providing a custom VERSION() is all you have to do. -- Regards, Aristotle If you can't laugh at yourself, you don't take life seriously enough.
Re: VERSION as (interface,revision) pair and CPAN++
On Thu, 2004-01-22 at 07:46, A. Pagaltzis wrote: * david nicol [EMAIL PROTECTED] [2004-01-22 03:43]: and rewriting Crequire in some way to respect that? Obviously you don't even need to; providing a custom VERSION() is all you have to do. Yes, this seems to be the Original Design. Where are the examples of custom VERSION() routines? Where is the VERSION template provided by h2xs? Where is the BCP concerning custom VERSION routines? What should it do? Croak when it cannot provide the requested interface? Carp and provide the old interface? I guess I'm all questions and no answers today, but I am going to start including VERSION routines in my modules from here on -- if only to get to write sub VERSION { -- david nicol In order to understand what another person is saying, you must assume that it is true and try to imagine what it could be true of. (George Miller; 1980.)
VERSION as (interface,revision) pair and CPAN++
On Wed, 2004-01-21 at 04:32, Fergal Daly wrote: A better (IMHO) alternative is to make the interface part of the version number as important as the name. This is equivalent to including it in the name except you don't lose information like you do when you just glue a number on the end of the name. You also get to use '.'s in the version number because you're not try to make a valid Perl module name. Then CPAN and other tools could understand the relationship between different versions of modules. Unfortunately, this is the bit I think will never happen, I don't think it would be possible to convince people that this is worthwhile, possibly because it's not worthwhile at this late stage. Q: was this suggestion made as a perl6 RFC and if so what did Larry think of it? Q2: Do you have a really clear idea of what split interface/version numbers would look like and is it light enough to try to get it into perl 5.9.1 ? (act fast) Could the situation be resolved by insisting that the first number in a version string is the interface version and following numbers are revision numbers, and rewriting Crequire in some way to respect that? For instance, require could take a regex as an argument and when that happens the version would have to match the regex, and the same with the VERSION element of Cuse If the VERSION argument is present between Module and LIST, then the use will call the VERSION method in class Module with the given version as an argument. The default VERSION method, inher ited from the UNIVERSAL class, croaks if the given version is larger than the value of the variable $Module::VERSION. So we're talking about altering the default VERSION method to recognize something other than a version string, that would trigger a different case. Such as, the major number has to match and the minor number has to be greater, or a PROVIDES method which defaults to the @{$Module::PROVIDES} array must include a version number with the same major number as the one we want. Or maybe we're talking about enlightened new versions of modules that present old interfaces when provided an old version number. This is IMO a non-starter, since it would require a lot of work that will not seem necessary by the people who would have to do the work. Or maybe we're talking about adding a bureaucratic layer to CPAN so it won't accept new versions of modules under the same name unless the new version passes the test suite from the old version, for modules on a restricted list that your module gets on once enough people rely on your module. The last suggestion would enforce interface compatability, after a fashion. It would need to be documented thoroughly so people don't go including test cases for things known to be broken in their modules, that verify that the modules are broken in the way that the modules are broken. (I have done this; DirDB 0.0.6 fails tests in DirDB 0.0.5 concerning croaking on unhandleable data types) Compliance might be part of a standard quality check before a module with a positive major version number is accepted; or CPAN might enforce quality ratings, which would be enforced to be nondecreasing, for a given module name. So if a module has a PRODUCTION quality rating, that means that the interface is guaranteed to remain stable into the future, under the given name. And that tests for things that are broken and you mean to fix in the future would have to be marked as such in the tests.pl. :) David Nicol -- david nicol In order to understand what another person is saying, you must assume that it is true and try to imagine what it could be true of. (George Miller; 1980.)