'Needless flexibilities' and structured records [very long]

R

Rainer Weikusat

NB: This is a lenghty multi-section texts starting with an explanation
of 'the basics' for anybody who might have been so unfortunate to
encounter Perl at version 5.16 or later. There's something which isn't
an explanation at the end. Any kind of constructive criticism is
welcome.

NB^2: I wrote this because I wanted to write it for some time. In line
with 'the base of any sound order is a large wastepaper bin' (Kurt
Tucholsky), "Stop shaking your dick in public, people are staring
already" and similar cheesinesses are just a short way into my
scorefile, alongside the large set of 'less than instructive posters'
who reside there already.


1. Objects, References and Methods
----------------------------------

One of the more unique features of Perl OO is that there is no such
thing as a unique, language-enforced representation for 'object
instances'. Perl has a number different kinds of builtin objects, the
most prominent ones would be scalars, arrays and hashes. glob objects
are used to store symbol table entries and also, to access file and
directory handles. code objects represent subroutines. There's also a
probably lesser-known one, namely, the lvalue object. Presumably (I
didn't test this) it is used as actual 'return value' of an lvalue
subroutine and it can also be 'caught in flight' when using certain
built-in subroutines/ operators, eg

perl -e '$w = "Hello"; $lv = \substr($w, 1, 3); print $lv, "\n"; $$lv = "aird"; print $w, "\n"'

will print

LVALUE(0x620d70)
Hairdo

References to objects of all these kinds can be created by applying
the backslash operator to an object of a certain kind or by using a
special 'anonymous object' construction syntax, eg

\%hash

return a reference to the hash %hash and

[1,2,3]

returns a reference to an anonymous array containing the values 1, 2
and 3.

Any reference can be associated with a package with the help of the
bless function. When a reference is associated with a package, the
syntax

$ref->name(argument, ...)

can be used to request a 'method call': This will search for a
subroutine named name in the package $ref is currently associated with
and in all packages this package declared as its parent
packages. Because this 'subroutine search' happens at runtime using
the string 'name' as key, this is really rather a message-passing
mechanism than something like the 'virtual methods' provided by C++.

The implication of this is that any Perl object can acquire OO-like
features via attaching a reference to the object to a package while
otherwise retaining its built-in behaviour.


Example: File Handle Objects
----------------------------

A relatively simple uncommon example: In a certain program, I'm using
Linux 'queued realtime signals' for I/O event notification. This
requires manipulating the file status flags associated with the
corresponding file descriptor (via fcntl) to set the owner of the file
and the signal which is supposed to be sent in case of an 'I/O
readiness' event for this file descriptor to to set or clear the
O_ASYNC flag when such signals should or shouldn't be sent. fcntl is
an all-or-nothing operation which changes all flags of a file
descriptor. In order to modify individual flags selectively, the
corresponding bits need to be set or cleared in an integer
representing the set of all flags bits. Since file handles are
nowadays usually references to anonymous globs, they can be blessed
into a package in order to support calling methods on them. In this
case, this would be an enable_async method which enables asynchronous
notifcations and a disable_async method. The current flag set is
stored in the scalar slot of the glob. Because the glob is stilla
glob, it can otherwise be used just like any other file handle.

,----
| This is what the Perl 6 OO design document refers to as 'It is too
| minimal': It supports too many features many people never use because
| they have no idea what they'd be good for (because
| $better_known_oo_language didn't provide them)
`----


2. And Records?
---------------

Unfortunately, something Perl doesn't provide is a complex datatype
whose components can be accessed by name instead of using an integer
(array) or string (hash) key. Such record or structure types (or
constructs conceptually derived from them) are usually used as object
representation in other languages, not the least because they nest
properly.

Example in C
------------

Assuming the type definitions

struct person {
char *forename, *surname;
};

and

struct citizen {
struct person person;
char *nationality;
}

and the variable definitions

struct citizen someone = { .person = { .forename = 'Zarathustra' }'};
struct person *person;

the person pointer can point to a struct citizen,

person = (struct person *)&someone;

and can be used to access the 'struct person' fields,

puts(person->forname);

This obviously lends itself to implementing 'data inheritance' in
addition to 'method inheritance.


Hashes perhaps?
---------------

An obvious idea ('the obvious idea') to provide a similar facility in
Perl are hashes because these are 'dictionary' (or 'associative array')
data structures mapping string keys to abitrary 'other' (scalar)
values.

,----
| At this point in the story, the PHP (or Java) programmer
| probably leans back with a sigh of relief because 'the solution' just
| presented itself and thus, there's no further requirement to think
| about all this complicated stuff just in order to "get shit done
| quickly" aka 'web development' (the 'get ...' is a quote from an
| article about 'web frameworks' I read a while ago).
`----

But often, hashes/ references to anonymous hashes are not really a
good choice for representing objects because 'a hash' is essentially
an allocation scheme for array slots suposed to map all members of a
'large' (or numerically discontinuous) set of 'keys' to such array
slots by running a (seriously) lossy compression function on the key
in order to transform it into a slot number based on the assumption
that the actual number of different keys in the hash will always be
much smaller than the number of elements in the key set and combining
that with a mechansism to deal with so-called 'hash collision', cases
where the result of the lossy compression function is identical for
two keys which are actually different. *If* the number of actual
collisions in the hash table is small, key insert, delete and lookup
operations performed on it will complete in constant time, although
they are still fairly expensive because of the compression
function. In order to ensure that the number of collisions will be
small, hash tables are usually (and possibly significantly) larger
than what would be required if the keys could just be packed into
successive slots of the table.

Another problem with using hashes as object representation is that the
'namespace' of each individual hash is 'global': If two related
packages use the same name for an object property, they will end up
using the same 'virtual property slot' and there's no easy way for
preventing that.

A possible way would be to prefix the package name to each property
name and hope that all other packages also do this. But since package
names are often longish, this is an unwiedly workaround. A workaround
for the workaround would be to use declared constants whose values are
the long property names and whose names are short enough to be used
comfortably. But - alas - the {} autoquotes the key if it 'looks like
a string' and this means that

use constant NAME => __PACKAGE__.'name';

$h->{NAME} = 'Paul';

doesn't work. A workaround for the workaround for the workaround would
be to invoke the 'constant subroutine' explicitly,

$h->{NAME()} = 'Paul';

but the two added noise characters are rather ugly.

Lastly, the hash lookup which is a rather costly operation happens
every time such a named property is accessed.


A Better Idea: Arrays
---------------------

Accessing slots of an array is a cheap O(1) operation and arrays don't
need to be larger than necessary to accommodate the number of elements
stored in them in order to workaround the fact that slots are
allocated based on the result of a (seriously) lossy compression
function applied to the key. Since the Perl OO system is 'too minimal'
aka 'provides to many weird features', array references can also
serve as 'invocant objects' for method calls.

Two remaining problems are

1. Obviously, accessing instance properties by index number is not a
good idea except in very trivial cases.

2. Slot allocation for related classes.

(1) can be solved by declaring named constants with integer values and
using these in the source code. The nice properties of this are that
names declared in different packages don't collide, that the
translation into slot number happens at compile time and that the []
require no synactical workarounds for this case.

use constant NAME => 0;

$h->[NAME] = 'Emil';

works as intended.

It is possible to solve (2) by manageing slot numbers manually and
such a scheme can be made to work for single-inheritance class
hierarchies by using a 'magical name' to refer to the last slot number
used by a certain package. Slot numbers for 'derived class' can then
be calculated relative to the 'last used slot' number of the
superclass. But this is still fairly inconvenient and the

use constant PORP_A => 0;
use constant PORP_B => 1;
use constant PORP_C => 2;

requires a lot of typing.


Automatic Compile-Time Allocation of Array Slots
------------------------------------------------

The

use something (...)

feature can be used to execute a subroutine named something::import at
compile time. By combining this with the existing constant module and
some fairly simple state-tracking code, it is possible to create slot
name constants based on a use statement in a way which works for
single-inheritance class hierachies:

---------------
package slots;

use feature 'state';
use constant;

sub import
{
state %counters;
my ($ctr, $fields);

if (ref($_[1])) {
$fields = $_[1];
$ctr = 0;
} else {
$ctr = $counters{$_[1]};
$fields = $_[2];
}

@_ = ($_[0], {map { $_, $ctr++; } @$fields});

$counters{caller()} = $ctr;

goto &constant::import;
}

1;
---------------

Assuming this code exists as slots.pm somewhere where perl can find
it, a non-derived class could declare a set of 'slot names' by doing

use slots [qw(FORENAME SURENAME)];

and a class derived from this class could do

use slots ('Person', ['NATIONALITY']);

('Person' being the name of the superclass package) to request an
additional field which will have the next 'free' slot number.

NB: This is an idea I had yesterday and since the implementation was
so exceedingly simple, I thought sharing it might be a good idea. I
expect that there situations this simple code doesn't handle but it is
'good enough' to be useful.
 
R

Rainer Weikusat

[...]
---------------
package slots;

use feature 'state';
use constant;

sub import
{
state %counters;
my ($ctr, $fields);

if (ref($_[1])) {
$fields = $_[1];
$ctr = 0;
} else {
$ctr = $counters{$_[1]};

You can look up the inheritance here instead of having the user specify
it (DRY and all that...). As long as they

Re: DRY

That's a cute acronym for 'Design pattern aficionados Reinventing
elementary database theorY' (in short: Databases should not contain
redundant copies of the same data to avoid so-called 'update
anomalies', the database becoming inconsistent because not all
redudant copies of 'some data' are changed during an update. People
usually ignore that in favor of 'nobdoy will ever update X without
going to gatekeeper code Z which always keeps all copies in sync' and
looking dedicatedly in another direction whenever somebody mentions
that 'updates withoug going throug Z' are possible. This could almost
be called 'a basic design principle of modern Linux distributions'
:->).
use parent "Parent";
use slots qw/one two three/;

in that order, @{ caller . "::ISA" } will contain the direct parents at
this point. You can also croak if they're trying to use MI.

I thought about that and decided against it: This would require @ISA
to be populated by the time the import method is executed and the code
would either need to operate in 'pushy nanny mode' ("I've told you you
must not inherit methods from more than one class and you must do as I say
because I say so!") or make a guess at which of the possibly many
direct and indirect(!) 'superclasses' is supposed to be the anchoring
point for the new slot name series. But in reality, @ISA is about
method lookups and not about 'allocating array slots in a
non-conflicting way to members of some "inheritance tree"'.

One can assume that packages sharing an array reference for instance
data will usually also share method via @ISA but this need not be
true. Also just because some package is listed in @ISA doesn't
necessarily mean that it should also participiate in the 'data
inheritance hierarchy'. These are orthogonal issues and one of the
serious drawbacks of many existing 'OO support modules' is that the
authors usually fell prey to their own "Jack of all trades" desires
and 'hard-coded' technically independent policy choices they usually
happen to make in their code. Eg, one cannot comfortably use parent.pm
except if one always keeps each package in a file of its own, to name
a simple example.

'Radio and television' are popular because 'they just work': Turn them
on, something's gonna happen, no need to worry about that. For the
same reason, complaining about the programme is also very popular.
$fields = $_[2];
}

@_ = ($_[0], {map { $_, $ctr++; } @$fields});

Strictly this should be "constant" rather than $_[0] (which will be
"slots"), since this is supposed to be emulating constant->import. In
fact constant.pm doesn't care, but in principle it might care at some
point in the future.

It would be 'constant' if constant::import had actually been
invoked directly. But since this isn't the case and no 'requirements
specification' of any kind exists (as far as I'm aware of that)
anything would do insofar it pushes the first 'actual argument' to
$_[1]. Using whatever was passed in $_[0] to this import routine is as
good an anything as anything else and might also be useful 'in
future'.
It's probably easier to just create the constants manually, rather than
bothering with goto-&:

for (@$fields) {
my $n = $ctr++;
no strict "refs";
*{ caller . "::$_" } = sub () { $n };
}

If you were feeling excited you could use the scalar-refs-in-the-stash
trick constant.pm uses to avoid a full GV+CV.

Ehh ... I'm using the 'scalar-refs-in-the-stash' trick and - for that
matter - any useful other 'trick' constant.pm might employ now or in
future. That's why I'm just telling constant.pm which constants I'd
like to have created[*].

This is also a design question: Since a subroutine providing the
feature I want to build on already exists and the implementation is
suitable for the task at hand, the code should rather invoke the
subroutine than be decorated by copy of some of its code (this is also
a 'DRY/ redundant copies of identical data' issue).

[*] I've been using Perl 5 for long enough that I was pleasantly
surprised when I could stop creating long

sub A_VALUE() { 1; }
sub ANOTHER() { 2; }

..
..
..

cascades ...

[...]
use slots ('Person', ['NATIONALITY']);

('Person' being the name of the superclass package) to request an
additional field which will have the next 'free' slot number.

NB: This is an idea I had yesterday and since the implementation was
so exceedingly simple, I thought sharing it might be a good idea. I
expect that there situations this simple code doesn't handle but it is
'good enough' to be useful.

It's not a bad idea; it's basically the same as Class::Accessor::Faster
or MooseX::ArrayRef.

I assume the authors of the two named modules would calls this 'a
backhanded compliment' (and rightly so :->) and so would I (also
rightly so :->). This is a slight generalization of an anonymous array
sharing scheme I've been using since 199x and it really doesn't (and
shouldn't) do anything except solve this particular problem.
As you note, it only works for single inheritance,
and doesn't work with MI or roles-which-provide-attributes (however they
may be implemented); it also fails (badly) if you attempt to modify the
inheritance or attributes of a class at runtime.

It can't 'fail badly at run-time' because the code never runs 'at
run-time'. This 'anonymous array sharing scheme' might not be useful
to solve 'certain problems' or might only be a component which could
be employed for solving them. But that's a different conversation (I
already wrote about the
washer-dryer-lawnmower-bicycle-tophat-eggwarmer-hovercraft-theodolite
.... sometimes, a hammer is really the right tool).
MI can be fixed by using accessor methods, since this allows a
subclass's implementation of ->foo to use a different slot from the
superclass's;

A more general remark: At one end of the 'OO spectrum', you have
something like CLOS or DYLAN (or Perl 6 or 'Perl With Mossy Antlers' or
....). These systems are based on an abstract, 'behavioural'
specification of what 'an object instance' actually is. Nobody except
the people who implemented the system knows that and its users can
only interact with object instances by using a procedural
interface. The obvious advantages of that would be that the object
representation the system uses can change, either in future version or
even at run-time, without affecting exising code.

Then, there are intermediate/ hybrid systems like C++ or Java (or C)
where the object representation was set in stone when the system was
created but 'application code' does have direct access to object
instances. This will usually be better for performance (although users
of CLOS-style system will usually believe/ claim that 'the compiler'
can 'optimize' this which may even be true .. but why use a lot of
seriously complicated 'compiler technology' to make educated guesses
at facts language users could easily have provided [and provided
correctly] instead?).

Lastly, on the opposite end of the spectrum, there's Perl 5 where users
are in total control of object representation because the system only
provides a (possibly hierarchivally structured) system for routing
'messages' to 'message processing routines' (I propose a new 'flashy
acronym' here, namely P5INAE --- for 'Perl 5 Is Not An Error'). The
benefit is that what exactly constitues an object can be tailored to
the problem at hand and the obvious drawback is that people actually
have to do that.

So far (evil anecdotical argument), I haven't been stopped by a brick
wall when using that for the problems I head to deal
with. Consequently, I'm not convinced that it needs to be replaced
with a system based on different policy choices, especially
considering that 'simple system for solving relatively simple
problems' are useful because many problems are relatively simple
(Corollary: Problems which only manifest themselves dreadfully in 'really
large systems' could be a sign that createing 'really large systems'
is 'a really bad idea').

I'm also a firm desbeliever in 'the run-time environment has to defend
itself agains one million evil typing monkeys'. That's a social
problem and the solutions are 'education' and 'natural selection'.
 
R

Rainer Weikusat

Ben Morrow said:
Quoth Rainer Weikusat said:
Ben Morrow <[email protected]> writes:
[...]
Re: DRY

That's a cute acronym for 'Design pattern aficionados Reinventing
elementary database theorY' (in short: Databases should not contain
redundant copies of the same data to avoid so-called 'update
anomalies', the database becoming inconsistent because not all
redudant copies of 'some data' are changed during an update. People
usually ignore that in favor of 'nobdoy will ever update X without
going to gatekeeper code Z which always keeps all copies in sync' and
looking dedicatedly in another direction whenever somebody mentions
that 'updates withoug going throug Z' are possible. This could almost
be called 'a basic design principle of modern Linux distributions'
:->).

Um, what?

You can get an explanation in more detail than I care to read at the
moment here:

http://en.wikipedia.org/wiki/Database_normalization

If you strip out the technicalities relating to relational database
management systems, you arrive at 'the DRY principle'.

[...]
I suppose; certainly a facility to inherit slots independently of @ISA
might be useful. Data and method inheritance staying in sync must be the
overwhelmingly common case, though, so optimising the 'use' interface
for that would seem sensible.

The issue is really that the @ISA array has a defined meaning: It is
used to facilitate subroutine sharing outside of the normal export/
import mechanism. This can be envisioned as being "a feeble/ strange
way to declare 'class ancestor/ descendant' relationships" and it is
usually even referred to as such but this is just 'a notational
convenience' and it really isn't something like this.

Eg, the most common case of 'multiple inheritance' I'm presently
dealing with is a package named 'Thing' and all it provides is an
overloaded ""-operation which prints the package some reference is
blessed into, followed by the refaddr, followed by a 'tag' of some
kind in []. A package which wants to inherit this "" needs to provide
a tag method returning a tag and the Thing package maintains a cache
of 'object tags' in a package-global hash. This is supposed to make
diagnostic messages more intelligible and the provided functionality
is independent of any properties of packages 'inheriting' from Thing.

'Optimising for the common case' is really the same as 'hardcoding
technically unrelated policy choices' because someone considered them
"overwhelmingly important" or even "the only Right choice" or so but
in practice, this still just means "How I happen would have done
that" and how justified the generalization actually is is usally
unknown.

Some people consider 'make the right policy ch

[...]
@_ = ($_[0], {map { $_, $ctr++; } @$fields});

Strictly this should be "constant" rather than $_[0] (which will be
"slots"), since this is supposed to be emulating constant->import. In
fact constant.pm doesn't care, but in principle it might care at some
point in the future.

It would be 'constant' if constant::import had actually been
invoked directly. But since this isn't the case and no 'requirements
specification' of any kind exists (as far as I'm aware of that)
anything would do insofar it pushes the first 'actual argument' to
$_[1].

The only documented interface to constant.pm is via 'use'. This will
call constant::import with $_[0] = "constant", so that is an implicit
part of the 'requirements specification' for using constant.pm.

'Implicit [part of] the specification' is a contradiction in
adiecto. When invoking constant::import via use, the first argument
happens to be 'constant'. Since nothing is documented about invoking
constant::import in any other situation, no information regarding what
the first argument is supposed to be then is available. One can argue
that the conservative choice would be to pass 'constant' as first
argument,

It's only by chance that constant::import happens to ignore its first
argument:

but given the code is written in the way it is, this isn't really
needed.

NB: The obvious counterargument would be that using goto & to hide
the caller but then pass information about it to the called routine
nevertheless is 'internally add odds with itself' but I just felt a
bit 'full disclosure anarchistic' when writing the code.

[...]
I was trying to be polite.

But you were actually impolite in all directions: Both modules you
named are supposed to provide 'something completely different' and in
the case of Class:: it isn't even clear (to me, at least) whether
this involves using anonymous arrays as object representation at all.
I was trying to be polite by formulating this in this way instead of
just pointing out that I strongly disagree with 'certain
implementation choices' implicit in Class:: and Moose::antyhing and
really don't appreciate something sensible I wrote being dragged into
these pits.
You were presenting it as a revelation the like of which Perl had
never seen before;

I presented a scheme for manageing array slots in shared, anonymous
arrays and while this scheme is very simple, it is useful and it hasn't
(to the best of my knowledge) been published in some 'canned',
ready-to-use form so far (except maybe as 'minor internal part' of
another Wolpertinger-module). Also, minus a few hints in the Perl
documentation, I'm not aware of any actual discussion of the
advantages and disadvantages of using different kinds of Perl objects
as object representation.
in fact, it's a rather obvious idea, with very few advantages over
standard hashref-based objects, and some important disadvantages.
In some specific situations, in particular where saving memory is
important, it might be useful, but for general use it doesn't seem
to me worth expending effort trying to work around the problems with
arrayref objects unless you want to do a complete job and solve all
the problems.

Please feel free to argue against the points in favour of using
anonymous arrays for object representation and against using anonymous
hashes I made. I consider them sound but argueing against them should
certainly be possible. The 'summary judgement plus hand-waiving' above
isn't useful for anyone trying to make an informed descision
himself. We already had 'one million flies' aka 'the standard way' aka
'but everyone [I consider relevant] does it that way' -- that's a
logical fallacy also known as 'ad populum' fallacy,

http://www.skepdic.com/adpopulum.html

and even your somewhat repetitive insistence of generalizing each
specific problem situation until the specific solution to that no
longer suits the expanded problem is sort-of a 'well-known'
'discussion technology' of less-than-excellent reputation,

http://www.whale.to/m/disin.html

in particular, 12/14 (and maybe 8 for the 'qualitative summary
judgement' trick).

[...]
Sometimes, yes. Most of those times, I'd just use an arrayref directly,
and probably not even bother with the constants.

Using magic, numerical values in code is something which should - in
my opinion - almost always be avoided, especially so if the actual
numerical values have no relation to their use (eg, for an anonymous
array used as object representation, it usuallg doesn't matter which
'instance property' is associated with which slot number).
 
R

Rainer Weikusat

Ben Morrow said:
Yes, I know what database normalisation is. I don't see what relevance
it has here, nor what relevance the design of Linux distros has.


So? I assume you're trying to make a point, rather than just talking to
hear yourself speak, but I can't see what it might be.

And I 'assume' that the point of this 'snide remark' is - well - being
a snide remark.
No, it's used to implement method dispatch. This is quite different from
sub sharing; for one thing, it uses a different call syntax.

Call this anything you like but it enables more than one 'package'
(call that 'class' if you like) to execute the same subroutine in
response to a so-called 'method call' and not about 'data
inheritance'.
This can be envisioned as being "a feeble/ strange
way to declare 'class ancestor/ descendant' relationships" and it is
usually even referred to as such but this is just 'a notational
convenience' and it really isn't something like this.

Eg, the most common case of 'multiple inheritance' I'm presently
dealing with is a package named 'Thing' and all it provides is an
overloaded ""-operation which prints the package some reference is
blessed into, followed by the refaddr, followed by a 'tag' of some
kind in []. A package which wants to inherit this "" needs to provide
a tag method returning a tag and the Thing package maintains a cache
of 'object tags' in a package-global hash. This is supposed to make
diagnostic messages more intelligible and the provided functionality
is independent of any properties of packages 'inheriting' from Thing.

This technique is called 'mixins' (at least, it is in the Perl
world),

[digression removed]

For the situation at hand, it was an example of a package listed in
@ISA which is not supposed to be part of the 'data inheritance
hierarchy'.
Optimising for the common case doesn't mean making functionality
unavailable, it just means making the 'easy' interface work the way you
usually want it to.

In the given case, the 'easy' interface does work in the way 'I
usually want it to' because I usually don't 'want' to force policy
descisions which seem sensible to me onto others. Not the least
because I would be forcing them onto myself first and they wouldn't be
suitable for my use case.

[...]
It does. Class::Accessor::Faster provides almost exactly the same as
slots: it gives you a constructor, and accessors, for an arrayref-based
object, and nothing else.

Compared to a 'pragamatic module' which provides a way to allocate
'array slot numbers' in a way suitable for using an anonymous array as
'object instance representation with data sharing' among members of
single-inheritance class hierarchy, that's something 'almost
completely different' except for the (irrelevant) implementation
detail that 'anonymous arrays' might also be used by Class::.
So you have said, though you have yet to explain your disagreement
beyond

[snide remark deleted]
Had you cared to look, you would have found that CAF is pretty-much
exactly the same thing.

It may be 'pretty much the same thing' in your opinion but this is an
opinion I don't share.

[more irrelevant stuff deleted]
I have given some of the disadvantages already:

- Inheriting attributes from more than one parent, by any means, is
more difficult to get right;

As I already wrote a couple of times: This is for single-inheritance
hierarchies. Consequently, the fact that it really isn't suitable for
'class hetarchies' is no more a disadvantage of this scheme than it is
'a disadvantage' of a car that it can't fly or swim: It isn't
supposed to.
- Changing the attributes of a class at runtime (including changing
the inheritance) will cause existing compiled methods to refer to
the wrong attributes;

I understand this as "code could be written with the explicit
intention to break this and then, it would break". Which isn't exactly
a surprise and holds for all code.

[yet more irrelevant stuff deleted]

BTW, if I had any talent as graphical artist (which I unforuntately
don't), I would scan the cover of the camel book, would decorate
the head of the poor beast with a pair of gigantic antlers (maybe with
some dripping bits of water plants of them) and would publish this as
a 'Remove doesn't fit into this picture' cartoon. I figure that
would leave you with a pair of antlers hanging in the air for no
particular reason and me with a camel. Just to return one of the snide
remarks.
 
R

Rainer Weikusat

Ben Morrow said:
Yes, I know what database normalisation is. I don't see what relevance
it has here, nor what relevance the design of Linux distros has.


So? I assume you're trying to make a point, rather than just talking to
hear yourself speak, but I can't see what it might be.

And I 'assume' that the point of this 'snide remark' is - well - being
a snide remark.
No, it's used to implement method dispatch. This is quite different from
sub sharing; for one thing, it uses a different call syntax.

Call this anything you like but it enables more than one 'package'
(call that 'class' if you like) to execute the same subroutine in
response to a so-called 'method call' and not about 'data
inheritance'.
This can be envisioned as being "a feeble/ strange
way to declare 'class ancestor/ descendant' relationships" and it is
usually even referred to as such but this is just 'a notational
convenience' and it really isn't something like this.

Eg, the most common case of 'multiple inheritance' I'm presently
dealing with is a package named 'Thing' and all it provides is an
overloaded ""-operation which prints the package some reference is
blessed into, followed by the refaddr, followed by a 'tag' of some
kind in []. A package which wants to inherit this "" needs to provide
a tag method returning a tag and the Thing package maintains a cache
of 'object tags' in a package-global hash. This is supposed to make
diagnostic messages more intelligible and the provided functionality
is independent of any properties of packages 'inheriting' from Thing.

This technique is called 'mixins' (at least, it is in the Perl
world),

[digression removed]

For the situation at hand, it was an example of a package listed in
@ISA which is not supposed to be part of the 'data inheritance
hierarchy'.
Optimising for the common case doesn't mean making functionality
unavailable, it just means making the 'easy' interface work the way you
usually want it to.

In the given case, the 'easy' interface does work in the way 'I
usually want it to' because I usually don't 'want' to force policy
descisions which seem sensible to me onto others. Not the least
because I would be forcing them onto myself first and they wouldn't be
suitable for my use case.

[...]
It does. Class::Accessor::Faster provides almost exactly the same as
slots: it gives you a constructor, and accessors, for an arrayref-based
object, and nothing else.

Compared to a 'pragamatic module' which provides a way to allocate
'array slot numbers' in a way suitable for using an anonymous array as
'object instance representation with data sharing' among members of
single-inheritance class hierarchy, that's something 'almost
completely different' except for the (irrelevant) implementation
detail that 'anonymous arrays' might also be used by Class::.
So you have said, though you have yet to explain your disagreement
beyond

[snide remark deleted]
Had you cared to look, you would have found that CAF is pretty-much
exactly the same thing.

It may be 'pretty much the same thing' in your opinion but this is an
opinion I don't share.

[more irrelevant stuff deleted]
I have given some of the disadvantages already:

- Inheriting attributes from more than one parent, by any means, is
more difficult to get right;

As I already wrote a couple of times: This is for single-inheritance
hierarchies. Consequently, the fact that it really isn't suitable for
'class hetarchies' is no more a disadvantage of this scheme than it is
'a disadvantage' of a car that it can't fly or swim: It isn't
supposed to.
- Changing the attributes of a class at runtime (including changing
the inheritance) will cause existing compiled methods to refer to
the wrong attributes;

I understand this as "code could be written with the explicit
intention to break this and then, it would break". Which isn't exactly
a surprise and holds for all code.

[yet more irrelevant stuff deleted]

BTW, if I had any talent as graphical artist (which I unforuntately
don't), I would scan the cover of the camel book, would decorate
the head of the poor beast with a pair of gigantic antlers (maybe with
some dripping bits of water plants on them) and would publish this as
a 'Remove wha doesn't fit into this picture' cartoon. I figure that
would leave you with a pair of antlers hanging in the air for no
particular reason and me with a camel. Just to return one of the snide
remarks.
 
C

C.DeRykus

...
A possible way would be to prefix the package name to each property

name and hope that all other packages also do this. But since package

names are often longish, this is an unwiedly workaround. A workaround

for the workaround would be to use declared constants whose values are

the long property names and whose names are short enough to be used

comfortably. But - alas - the {} autoquotes the key if it 'looks like

a string' and this means that
use constant NAME => __PACKAGE__.'name';



$h->{NAME} = 'Paul';

doesn't work. A workaround for the workaround for the workaround would

be to invoke the 'constant subroutine' explicitly,



$h->{NAME()} = 'Paul';


but the two added noise characters are rather ugly.

Maybe a sightly prettier workaround for auto-quoting
and all the sub calls...

use CONSTANT NAME => __PACKAGE__ . 'name';
$h->{+NAME} = 'Paul';

--> $VAR1 = {
'main::name' => 'Paul'
};
 
R

Rainer Weikusat

C.DeRykus said:
]
use constant NAME => __PACKAGE__.'name';

$h->{NAME} = 'Paul';

doesn't work. A workaround for the workaround for the workaround would
be to invoke the 'constant subroutine' explicitly,
$h->{NAME()} = 'Paul';
[...]

Maybe a sightly prettier workaround for auto-quoting
and all the sub calls...

use CONSTANT NAME => __PACKAGE__ . 'name';
$h->{+NAME} = 'Paul';

Something which also works, although I'm not sure if this is
intentional:

$h->{NAME,}
 
R

Rainer Weikusat

Rainer Weikusat said:
C.DeRykus said:
]
use constant NAME => __PACKAGE__.'name';

$h->{NAME} = 'Paul';

doesn't work. A workaround for the workaround for the workaround would
be to invoke the 'constant subroutine' explicitly,
$h->{NAME()} = 'Paul';
[...]

Maybe a sightly prettier workaround for auto-quoting
and all the sub calls...

use CONSTANT NAME => __PACKAGE__ . 'name';
$h->{+NAME} = 'Paul';

Something which also works, although I'm not sure if this is
intentional:

$h->{NAME,}

.... but this is generally not a good idea (which leads to another
disadvantage of hashes for emulating structures): Since $h->{NAME} is
a valid expression in its own right, accidentally omitting the , (or
+) possibly means using an 'unintended' key for storing or accessing a
certain property. A similar issue exists for 'plain string keys'
because a name with a typo is as good a hash key as any other
string. Since 'objects properties' are really nothing but a
generalization of 'global variable sets'[*], that is 'modifiable objects
multiple cooperating subsroutines used for storing shared state
information', this becomes especially nasty when dealing with code
written by someone else ('someone else' here includes 'code one has
written oneself some months or even years ago'): The only way to
determine if a particular instance of

$h->{some_name}

is using the intended key string or an erroneous one is to examine all
code which might use the object in order to find all 'similar keys'
and then make an educated guess based on the information determined in
this way. Yet another 'similar problem' exists for 'accessor methods'
because lookup of these is also based on searching for a string key in
some set of hash tables: An accidentally misspelled method on a rarely
executed codepath will go unnoticed until this codepath is executed
and then, a fatal runtime error will occur.

In contrast to this, declaring property names as constants and using
these constant for accessing array slots implies that misspellings etc
will cause compile-time errors (when using strict as part of the
compilation).

[*] Another fact 'many people' who pay very demonstrative lip service to
"global variables are BAD" refuse to understand (Or claim to be
unable to understand. I haven't yet made up my mind on whether this is
a genuine incapability to see structural similarities in superficially
different things or a case of 'playing dumb' in the hope that
'stubborn denial' combined with 'suitable' personal attacks will at
least be sufficient to confuse someone).
 
C

C.DeRykus

Rainer Weikusat said:
use constant NAME => __PACKAGE__.'name';

$h->{NAME} = 'Paul';

doesn't work. A workaround for the workaround for the workaround would
be to invoke the 'constant subroutine' explicitly,

$h->{NAME()} = 'Paul';
Maybe a sightly prettier workaround for auto-quoting
and all the sub calls...

use CONSTANT NAME => __PACKAGE__ . 'name';
$h->{+NAME} = 'Paul';
Something which also works, although I'm not sure if this is
intentional:

$h->{NAME,}



... but this is generally not a good idea (which leads to another

disadvantage of hashes for emulating structures): Since $h->{NAME} is

a valid expression in its own right, accidentally omitting the , (or

+) possibly means using an 'unintended' key for storing or accessing a

certain property. A similar issue exists for 'plain string keys'

because a name with a typo is as good a hash key as any other

string. Since 'objects properties' are really nothing but a

generalization of 'global variable sets'[*], that is 'modifiable objects

multiple cooperating subsroutines used for storing shared state

information', this becomes especially nasty when dealing with code

written by someone else ('someone else' here includes 'code one has

written oneself some months or even years ago'): The only way to

determine if a particular instance of



$h->{some_name}



is using the intended key string or an erroneous one is to examine all

code which might use the object in order to find all 'similar keys'

and then make an educated guess based on the information determined in

this way. Yet another 'similar problem' exists for 'accessor methods'

because lookup of these is also based on searching for a string key in

some set of hash tables: An accidentally misspelled method on a rarely

executed codepath will go unnoticed until this codepath is executed

and then, a fatal runtime error will occur.



In contrast to this, declaring property names as constants and using

these constant for accessing array slots implies that misspellings etc

will cause compile-time errors (when using strict as part of the

compilation).

...

I regretted the post almost immediately for several reasons.

However, I was surprised by these exceptions to these hash key
auto-quoting twists. It seems very odd that the trailing ","
(multi-dimensional array emulation) would work at all. Ditto
for the pre-pended "+". No idea why.
 
J

John W. Krahn

Ben said:
The one thing that is slightly weird is that $h{NAME,} calls NAME in
scalar context, despite the comma.

The comma has no effect on context. NAME, is inside $h{} and $h{} is a
scalar (see the $ sigil). If you had used a hash slice like @h{NAME,}
then NAME would be in list context, with or without the comma.

I think what is happening here is
that the code which decides whether this is a single- or multi-dim
subscript just looks at the number of expressions in its argument list,
by which time that comma has long since disappeared.


John
 
C

C.DeRykus

Anything inside the {} which doesn't match \w causes the contents to be

interpreted as an expression rather than a string. C<+NAME> and C<NAME,>

are both perfectly valid expressions which call the sub NAME: the + is

the unary 'term' operator, which does exactly nothing and is

occasionally useful to persuade Perl to choose one interpretation of an

ambiguous expression rather than another;the , is an ordinary trailing-comma-on-the-end-of-a-list, which is eaten by the parser and doesn't add

an extra element.


The one thing that is slightly weird is that $h{NAME,} calls NAME in

scalar context, despite the comma. I think what is happening here is

that the code which decides whether this is a single- or multi-dim

subscript just looks at the number of expressions in its argument list,

by which time that comma has long since disappeared.


Thanks for the insights. I actually knew "+" was a "persuader"
but had forgotten I saw this in the doc:

Use $hash{CONSTANT()} or $hash{+CONSTANT} to
prevent the bareword quoting mechanism from kicking in.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top