Re: VERSION as (interface,revision) pair and CPAN++

2004-01-30 Thread Adrian Howard
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++

2004-01-30 Thread khemir nadim
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++

2004-01-30 Thread Fergal Daly
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++

2004-01-29 Thread Fergal Daly
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++

2004-01-29 Thread Fergal Daly
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++

2004-01-29 Thread Adrian Howard
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++

2004-01-28 Thread Lincoln A. Baxter
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++

2004-01-25 Thread Fergal Daly
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++

2004-01-22 Thread A. Pagaltzis
* 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++

2004-01-22 Thread david nicol
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++

2004-01-21 Thread david nicol
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.)