in reply to Re: This is why I use Perl
in thread This is why I use Perl

Good question.

If you allow, let me answer it by giving a simple example. Say we have a class that represents date, for example 2003-11-07, and one of its property is month.

Replies are listed 'Best First'.
Re: Re: Re: This is why I use Perl
by tilly (Archbishop) on Nov 08, 2003 at 16:10 UTC
    In some languages (eg Ruby) this isn't a good reason since it is trivial to implement get/set methods that will be visible and manipulatable outside of the class exactly how the old attribute was. So you can start with every property public, and then fix ones which in retrospect shouldn't have been. So you don't worry about it until you have to.

    You can do the same in Perl if you are willing to retrofit a tie on an existing class. (Admittedly at a considerable speed hit.) This is sufficiently obscure and confusing that the decision to do it is certainly making demands on the maintainance programmer.

    I don't know Java well enough to know whether this strategy is possible there, but I strongly doubt it. (Corrections or confirmation welcome.)

      Disagreed.

      To me this is not about the ability or capability of a given language, but about the principles of encapsulation, OO programming and the best practice for OO programming. You don't do things that's doable, if they are not the best practice.

        Disagreed.

        To me this is not about the ability or capability of a given language, but about the principles of encapsulation, OO programming and the best practice for OO programming. You don't do things that's doable, if they are not the best practice.

        I strongly suspect that you misunderstood the point of what I was trying to say. Probably because I didn't describe it in enough detail for you to clearly visualize what I described. So here it is in detail.

        In Ruby it is customary that if you want to write get/set methods for an attribute named, say, bar, you name the get method bar, and the set method bar=. The reason is that Ruby has a nice piece of syntactic sugar, foo.bar = baz is translated into foo.bar=(baz). Therefore people can use your get/set methods in a simple way. Now lets take a simple piece of working Ruby to show this in practice:

        # Example simple class class Foo def initialize (aBar) @bar = aBar; end def bar @bar; end def bar= (aBar) @bar = aBar; end end # Example usage foo = Foo.new("Hello"); puts foo.bar; # prints "Hello\n" foo.bar = "World"; puts foo.bar; # prints "World\n"
        And you see that you have normal get/set methods under the hood, in which we don't do much. They are, in fact, equivalent to the ones that you wrote above.

        Now let me write the same class in a different way:

        class Foo attr_accessor :bar; # Could add , :baz, :and, :others... def initialize (aBar) @bar = aBar; end end
        This is exactly the same as what I already had, except that I did less writing and the necessary bar and bar= methods will be implemented natively and hence run a bit faster.

        Therefore in Ruby if you have decided to write loads of public get/set methods, there is absolutely no reason to write them yourself. Just declare a bunch of public (yes, Ruby allows you to differentiate between public, private, and protected) ones with attr_accessor instead. If one of those accessors needs to do something more complex later (like add validation), that is easily enough changed later, transparently to existing code.

        The decision about whether to have public get/set methods is an entirely tangential question.

        In Perl you can do something similar, but the case for doing it is far weaker than it is in Ruby. If you have created your objects like most people do, as anonymous hashes, then you can move the implementation elsewhere and replace the anonymous hashes with anonymous tied hashes. You now get the ability in your FETCH, STORE, DELETE etc methods to hide specific fields, sanity check others, etc. All transparently to existing code.

        One way that you could do it is take what I did at Class::FlyweightWrapper and modify it by having the wrapping objects be references to tied anonymous hashes (with an arbitrary tying class) rather than anonymous scalars. This is doable, but takes a fair amount of work, and as I said, is clearly a suboptimal approach. I might consider it as a retroactive fix for a bad situation, but I wouldn't plan on doing things that way.

        In Java if someone has decided to allow the properties of an object be directly exposed, I don't believe that you can later replace that with get/set methods without having to visit existing code that used the old API. Therefore the possibility that you might some day, maybe, want to change your implementation and do something sophisticated in your get/set methods means that you must now hide properties and write get/set methods.

        Therefore while the principles of encapsulation, OO programming, etc do not change between languages, best practices may. In particular what is best practice in Java (writing lots of simple get/set methods) is not best in Ruby because the capabilities of the language give you a better option. In Perl if you get it wrong, you theoretically can fix things afterwards, but probably don't want to because the ways of doing it are problematic.

        The problem with overused phrases like "Best practice", is that they are so nubulous. The first problem is that you could seat 20 people in a room and say "We are going to tackle this project using best practices. Is everyone agreed?" and you would probably get 20 nods of agreement, all sincere. But if we could project each of the 20 mental visions of what that entailed into pictures, there would probably be 20 different pictures. There would be elements of agreement, but also elements where one person "Best practice" is another's "Not over my dead body!".

        Extreme example. A son get called to active service in a war zone. Waving him off, the father gives him the best advice he can think of. "Stay alive!". On the surface, that's pretty hard to argue with. Then why do we give so many postumuos medals for bravery? Sometimes best practice defies generalities.

        In programming, one persons best practice can be anothers nightmare and vice versa. Almost every new language, methodology, paradigm, culture or fad that comes along beleives it has found the "One right way". Some are more successful than others at gaining mindshare or market share, but every one is eventually superceded. One of the biggest reasons I like perl is that it doesn't ever claim, or even suggest that it is the only way, although there are those perl practitioners that try to. In fact, it promotes TIMTOWTDI from the outset.

        Like everything else, Perl as we know it now will be superceded. There will be those that hang on to the past, and what they know, for fear of the new, but it will happen anyway. The best we can hope is that the same mind that gave us the perl we know, will be able to resist the pulls in every direction, and the voices that insist that there P6 must become their vision of the one true way, and stick with his previous successful formula of enabling many ways whilst dictating few.


        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "Think for yourself!" - Abigail
        Hooray!
        Wanted!

Re: Re: Re: This is why I use Perl
by bart (Canon) on Nov 08, 2003 at 23:37 UTC
    Example: first we set day to 30. All is well. Next we set month to 2. All is still well, except that the 30th of February isn't valid.

    Let's do it the other way around: currently the month is set to 2. We try to set day to 30, but the system won't allow it, because the 30th of February isn't valid. We were going to set the month later, you bozo system!

    Lesson to be learned: don't let the system do your thinking.

    I don't like overzealous range testing for each individual field, in this example because the ranges themselves are interdependent. Having a get/set interface won't help one single bit.

    Instead, I only want the system to check if the date is valid, when I'm trying to use the date (in case of a Javascript on a HTML form: when trying to submit the form). In perl, for often used checks, you still can have some speed boost by caching, either by checking if the date has changed since last time, before checking again, or some use of a Memoize cache on the function value.

      No, you don't. Check-when-setting is to check-when-using as a compile time check is to a run time check. We want as much as possible of the former, so we get alerted when the problem happens, rather than hours later when any connection to its source is lost.

      What you describe, however, is a case of a stupid interface. The problem you described occurs when you want to set several parts of the data structure, but the steps are checked for validity separately. The answer is there needs to be a way to signal that a series of changes is atomic. In this case, if you are doing this in Perl, it can be achieved very easily by offering a method like

      $foo->set_date( day => 30, month => 2 );

      Designing good interfaces is hard but very fundamental.

      Makeshifts last the longest.

      the ranges themselves are interdependent.

      If fields are interdependent, don't you want to have a setter for all the fields, not just one field?