Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

Perl OOP

by QueenSvetlana (Novice)
on Jul 04, 2017 at 23:21 UTC ( [id://1194172]=perlquestion: print w/replies, xml ) Need Help??

QueenSvetlana has asked for the wisdom of the Perl Monks concerning the following question:

Hello, new to the forum!

I just received my copy of Learning Perl: Making Easy Things Easy And Hard Things Possible in the mail and have a copy of Modern Perl. I'm excited to learn Perl. I come from a background of Java/C# and I have a few questions about Perl.

My questions relate to Perl 5.
  1. When creating classes in Java/C# you can prohibit inheritance by using the final/sealed keywords, does Perl have such a keyword or technique?
  2. In Java/C#, when creating classes, it's a good idea to implement equals/GetHashCode methods, are these methods required when building Perl objects?
  3. Does Perl have the concept of Generics?

Replies are listed 'Best First'.
Re: Perl OOP
by tobyink (Canon) on Jul 05, 2017 at 00:33 UTC

    1. No, because that's a terrible idea. If I've created a class, why shouldn't you be able to inherit from it?

    2. Perl objects are (by default) equal when they refer to the same memory address. It makes sense to override this if you have a more useful definition of equality.

    3. No, Perl is weakly typed so has no need for anything equivalent to generics. There's little point in creating a List<T> template if lists accept all data types by default.

      1. No, because that's a terrible idea. If I've created a class, why shouldn't you be able to inherit from it?
      Because it may not be safe to do so, e.g. from Method Privacy in Perl
      Recently I was writing a subclass of somebody else's class and noticed it suddenly started behaving strangely. After an embarrassing amount of time spent debugging I discovered that one of my underscore methods happened to have the same name as one of the superclass' underscore methods (actually two levels up inheritance), so was getting called by the superclass when I hadn't expected it to be.
      Whereas a construct for disallowing inheritance (on a per class basis) would make it clear to a would be subclasser that the class wasn't designed for inheritance.

      But the OP was asking about how (as an author) they can stop their classes from being subclassed. I think this is totally legitimate from a design point of view given all the design warts of inheritance:

      • introduces tight coupling
      • breaks encapsulation
      • increases cognitive load

        But the OP was asking about how (as an author) they can stop their classes from being subclassed. I think this is totally legitimate from a design point of view

        I think it's as legitimate as asking how I, as a car manufacturer, can stop the people who buy my cars from hanging fuzzy dice from the rear-view mirror. Or how I can stop them from keeping their muddy boots on the back seat.

        It's none of my damn business. If I don't want them doing that, I shouldn't sell the car. If I really need the sale, but still don't want them doing it, I can document that it voids the warranty for their expensive leather seats.

      Thanks for the reply! In the case of the #1, when creating immutable objects, it's good practice to prohibit inheritance.
        AFIK, there is no such thing as an "immutable object" (an object where the parameters cannot be modified) in Perl. Perhaps there is such a thing in Java or C#. I don't think such a thing exists in C++. Perl is not Java, C# or C++. I guess if you have an object with 'name' as a property when that object was created, just don't expose set_name() in the public interface? If there is no function to change something, and you "play by the rules", then you can't change it. However there are ways in Perl to circumvent any rule and wind up with obscure code that changes a parameter that is not part of the public interface. I think that is also true in C++. My experience with Java is limited and experience with C# non-existent.

        I perhaps don't understand your question, but if you want an object which can be created but not modified after its creation... just don't expose any "set" functions for parameters within that object - set all of the params in the "new" method. Nothing in Perl that I know of will prevent you from inheriting from such an object.

        I would think that normally your object should have a "has a" relationship to an immutable object rather than your object being an "is a" relationship to said object. Perl will allow you to do something stupid.

        I don't see why that would be good practice.

        Let's create a telephone class:

        package Example::Phone { use Moo; has number => (is => 'ro', required => 1); sub call { ... } }

        Now let's use it:

        use Example::Phone; my $dad = Example::Phone->new(number => '+44 123 456 7890'); say "Dad's number is ", $dad->number; $dad->call();

        The object is immutable, in that once it's been instantiated, you can't use its public API to alter the number string.

        But that shouldn't stop us from subclassing it:

        package Example::Phone::Mobile { use Moo; extends 'Example::Phone'; sub send_sms { ... } }

        It doesn't in any way compromise the immutability of the parent class.

Re: Perl OOP
by haukex (Archbishop) on Jul 05, 2017 at 12:48 UTC

    The other monks have already provided some good answers, I just wanted to add a few more thoughts:

    1. Quoting the Camel (the Perl book, which I can highly recommend especially since you already have a coding background), Chapter 11:

      Perl does not automatically patrol private/public borders within its modules - unlike languages such as C++, Java, and Ada, Perl isn't obsessed with enforced privacy. A Perl module would prefer that you stay out of its living room because you weren't invited, not because it has a shotgun.

      I know this isn't exactly the topic you're asking about, but I think it expresses this part of the Perl philosophy quite well. In Perl, you can generally reach into any classes' internals if you wanted to, extend them with monkey patching, etc., and class authors usually won't try to stop you. But if you break something by doing something you shouldn't have, it's your fault :-) (Update: I should make it more clear that although you could do the aforementioned things, they are certainly not recommended for readable/maintainable code!)

      This idea extends into other areas as well: sure, you could make all your constants Readonly, but OTOH, you could trust that others will see variable names in all caps and know that they shouldn't be modified; if they do then anything that breaks is their fault. Because I myself tend to code defensively, I can tell you that this idea can take some getting used to. (As an aside, it should be noted that even in Java, sometimes it's necessary to look into an Object's private variables, and that is indeed possible with its Reflection API.)

    2. AFAIK, the primary purpose of Java's hashCode is to assist with storing objects in Java's hash tables. In Perl, hashes are indexed by strings, and any variable you try to use as a hash key will be forced into a string form. You also normally wouldn't use an object as a key for a hash - plain objects will stringify to something like "Object=HASH(0xa81262)" - although theoretically unique because it's based on the memory address, it's not really useful for lookups etc. Normally one would choose a useful string as the hash key. (There are of course some objects that stringify to useful values and that can be then turned back into those objects from strings, for example Math::BigInt, or DateTime via e.g. DateTime::Format::Strptime.)

      As for equals, note that even in Java there is often a problem with defining this method properly - e.g., are two ORM objects equal when they have the same primary key, or when all of their other fields match, or some combination of both? And then there are lots of cases where it doesn't even make sense to compare objects for "equality" other than comparing their memory addresses. So it has to be defined on a case-by-case basis, and if you want to compare Perl objects, it's the same, each class will have to define its own method to answer the question "am I equal to some other object?" - Perl does not place any requirement on this. (There is also the possibility to overload objects so that they can be compared with operators like eq, but this is an advanced topic, and many Perl classes don't use it.)

    3. No, as others have said, this isn't usually necessary in Perl. Let's say you have an object that stores other objects, you can just say: $collection->get($index)->some_method(), and the ->some_method() call will be looked up dynamically on the object that ->get() returns, regardless of what it is. (You can even do slightly wacky things like store the method name in a variable and say $collection->get($index)->$some_method_name. Update 2: Again, not recommended for normal practice, I'm just using this as an example as to just how dynamic method lookups are.)

      If you wanted to make sure that your "collection" only contains objects of one class, then you'd have to add code to enforce this yourself (lots of different ways to do that, also depending on whether an OO framework is being used, so I won't get into that here). Perl's native types like @arrays and %hashes don't have any mechanism like generics, although if you wanted to get into advanced topics, then it's possible to use tied variables that could enforce this - but I haven't yet seen that level of defensive coding.

    In general, for getting into Perl OO programming, I think Modern Perl is a good choice. When you read it, you might notice that Perl's objects can actually be very simple: they start out as references to anonymous hashes (usually), that are then "blessed" into their class. There aren't many other requirements for objects, you can even freely choose the name of their constructor, it doesn't have to be new. And you'll probably notice that unlike languages like Java, not every script/program needs to be OO, so my suggestion would be to first work on some Perl scripts without OO, then learn about blessed references (e.g. from the chapter "Blessed References" in Modern Perl), and then look at full-featured OO frameworks like Moose or Moo.

Re: Perl OOP
by stevieb (Canon) on Jul 04, 2017 at 23:24 UTC

    1) No.

    2) No.

    3) Please elaborate on what Generics is.

    See: perlootut, and perlobj.

      In a nutshell, generics enable types (classes and interfaces) to be parameters when defining classes, interfaces and methods. Much like the more familiar formal parameters used in method declarations, type parameters provide a way for you to re-use the same code with different inputs. The difference is that the inputs to formal parameters are values, while the inputs to type parameters are types. - From Oracle about Java generics
        In a nutshell, generics enable types (classes and interfaces) to be parameters when defining classes, interfaces and methods.

        Technically, a constructor in one class could create an object of another class, but unless the other class was a child class of the constructing class, it would not have access to the methods of the constructor's class.

        This would make more sense for a "contains" relationship between the classes. In that case, the constructor method could have a parameter specifying the class of a contained object. It could then call the constructor of that class to create the object and save a reference to that object in a field of it's own object-under-construction.

        Code in the container class, when operating on the contained object, would, auto-magically, invoke the methods of the contained object's class. However, code in the contained object's class would not have access to the containing class's methods.

Re: Perl OOP
by Arunbear (Prior) on Jul 05, 2017 at 11:17 UTC
    Inheritance can be discouraged by using the one arg form of bless or by using the technique of using separate packages for class and instance methods described in Re: OO - best way to have protected methods (packages). Here is another example:
    use strict; package Queue; sub new { bless [], 'Queue::__Internal'; } package Queue::__Internal; sub push { my ( $self, $item ) = @_; push @{$self}, $item; } sub pop { my ( $self ) = @_; shift @{ $self }; } sub size { my ( $self ) = @_; scalar @{ $self }; } 1;
Re: Perl OOP
by choroba (Cardinal) on Jul 07, 2017 at 13:39 UTC
    Ad 3: As Moose introduces some types (but no compile time checking, sorry), you can simulate kind of Generics with parameterized roles:
    #!/usr/bin/perl use warnings; use strict; { package MyCollection; use MooseX::Role::Parameterized; parameter name => ( isa => 'Str', required => 1, ); parameter member_type => ( isa => 'Str', required => 1, ); role { my $p = shift; has $p->name => (is => 'ro', isa => 'ArrayRef[' . $p->member_t +ype . ']'); }; }; { package MyClass; use Moose; with MyCollection => { name => 'list', member_type => 'Int' }; __PACKAGE__->meta->make_immutable; } my $obj = 'MyClass'->new(list => [ 1, 2, 3 ]); my $invalid = 'MyClass'->new(list => [ 1, 2, 3.14 ]); # Throws as 3.14 + is not an Int.
    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
      use Types::Standard qw(Int); tie my @list, Int; push @list, 1; push @list, 2; push @list, 3.14; # dies
A reply falls below the community's threshold of quality. You may see it by logging in.
A reply falls below the community's threshold of quality. You may see it by logging in.
A reply falls below the community's threshold of quality. You may see it by logging in.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1194172]
Approved by davies
Front-paged by cguevara
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others goofing around in the Monastery: (7)
As of 2024-04-23 13:30 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found