Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

Benefits of everything is an object? Or new sigils?

by LanX (Saint)
on Mar 01, 2010 at 23:21 UTC ( [id://826026]=perlmeditation: print w/replies, xml ) Need Help??

I was recently told that the "everything is an object" paradigma wouldn't have benefits in Perl...

Well actually I was never advocating this approach, but then I started meditating about the following (and maybe not too abstract) situation of coding phases leading to massive refactorings:

Phase 1

I have an array like structure - let's call it @employees - and I'm using in in dozens or hundreds of code positions, pushing to and shifting from it.

Phase 2

After a while I discover I need to extend this structure to check new elements.

E.g. when I need to guaranty that employees are never doubled after changing the family name (e.g. after a marriage) I need to check the name while storing¹.

In JS I would do something like deriving a new constructor² Employees() from Array() and extending it's push() method to allow this extra checking. Then I would only need to change the declaration of employees.

But what can I do in Perl without refactoring the code at dozens of positions?

Well thankfully there is Tie::Array, I'm able to tie a package "Employees" to @employees overriding the PUSH() method.

(You see ALL primitives in Perl already HAVE a back door to be extended like objects!)

Phase 3

Weeks after I discover that it would be handy that @employees was a full object with special methods - lets say something like ->list_active() and ->list_retired().

This wouldn't be a problem in JS, I would simply add these new methods to Employees().

But now I can't do this without refactoring the Perl code, because I can't bless @employees into a new package allowing these methods. I can only bless references and only call methods on these references.

The best thing I would be able to do is blessing \@employees and write \@employees->list_retired() whenever I need this new object...

Rather ugly isn't it?

Perlish workarounds

So what can I do to avoid this situation?

  • One solution would be to always start right from the beginning with a array-reference $employees and always write push @$employees_ar, "name". Consequently avoiding any use of the @ sigil.

    Here we get into a rather paradox situation in Perl, while push is prototyped (\@@) to work internally with an array_ref, the parser doesn't accept references ... that means I have to explicitly prepend a @ to every ref to make commands like push work... hmm not very handy...

  • There is a possibility to overwrite CORE::push such that it gets a new prototype allowing scalars. But this would not only disable checking at compilation time, it would reasonably slow down any push operation at all arrays ...

  • Another approach would be to use something like autobox and to always have objects right from the beginning and to write $employees_ar->push() !

    But that's

    a) not very perlish and

    b) not faster than overwriting CORE::push (I think even much slower)

    But this shows the benefit of everything is an object that other languages have!

  • Conclusion

    There doesn't seem to be a good solution in plain Perl to extend a data structure and avoiding

  • refactoring
  • universally slowing down operations (like push for all arrays)
  • leading to massive use of extra sigils and (de)referencing syntax.
  • being rather complicated and totally confusing for newbies...

    Especially the last point is important, a JS user has only to learn how to add new methods to an object constructed from Array() without having to fiddle around with

  • sigils syntax
  • (de)referencing sytax,
  • Tie:: and
  • CORE::

    And even most experienced proggers wouldn't do any of this and start to refactor the whole array code into employees objects!

  • Utopia

    The only way I can think of to elegantly solve this situation and to stay compatible to the perl culture is to extend the number of sigils or twigils in future perl versions.

    If we had something like €employees or  $@employees noting scalars which have to hold an array reference, then the parser could be extended to always accept this reference where an array is wanted!

    I.e. if the prototype of a function explicitly asks for an array, like with push @array,"elem", then push €array_ref,"name" should also work.

    Now tieing €array_ref would solve our 2nd phase problem.

    And blessing would allow to call methods like €employees->list_actives() for our 3rd phase problem.

    All this without complicating the syntax, refactorings or slowing down operations.

    (Of course it would be nicer if tieing and blessing of primitives could be unified into one interface like the one showed in autobox ... please note: JS has internally primitives, only the interface is OO)

    Further meditation

    Sigils are a wonderful invention in Perl to immediately show the type of a variable, also for compile time checking and naming convention!

    But unfortunately in most cases of longer programs - e.g when passing around or when nesting structures - proggers are rather quickly forced to use references, which all have the $-sigil.

    Like this the implicit naming convention of @ gets lost, which also leads to workarounds like appending something like _aref to the variable name...(see PBP)

    Consequently extending the sigil system to all known ref types would facilitate many situations without braking old code.

    Cheers Rolf

    Footnotes:

    ¹) of course it's a stupid design decision to use the family name as a key ... it's just an example, think abstract! :) UPDATE: ARGH and even more stupid is talk about a string key when using an Array (I was tired) ... thanks to BrowserUK for pointing this to me! :) ²) the object model in JS is different, just think something like "class" or "package" when I say "constructor".

    Replies are listed 'Best First'.
    Re: Benefits of everything is an object? Or new sigils?
    by Jenda (Abbot) on Mar 02, 2010 at 10:58 UTC

      How often do you actually end up wanting to add methods to something that started as a plain old datastructure? I think you invent a convoluted solution to a convoluted example.

      BTW, you forgot one solution. You do not have to bless the reference to a tied array. There already is a blessed reference under the covers. Get it by tied(@array) and call whatever you need on that.

      Jenda
      Enoch was right!
      Enjoy the last years of Rome.

        > How often do you actually end up wanting to add methods to something that started as a plain old datastructure?

        When it happens and you need to refactor the code, it's a real pain in the @$$ ...

        I think you don't see this situation, because you already incorporated all the ugly workarounds necessary.

        > I think you invent a convoluted solution to a convoluted example.

        Nope, actually I am spending lot's of time planing in advance to avoid this situation. (And I don't think I'm alone)

        Other can just calmly start to code and easily react afterwards. That's one of the big advantages of OOP.

        > Get it by tied(@array) and call whatever you need on that.

        OK so I can extend the tied package with methods intead of creating a new one... well certainly an improvement. Thx! 8)

        But do you really consider writing something like this

         tied(@employees)->list_actives()

        to be more readable than

         \@employees->list_actives()

        or even

         $employees_ar->list_actives()

        ???

        Cheers Rolf

          No. I consider list_active_employees(\@employees); readable enough.

          You have to invest some time at the beginning to design your data structures and objects and decide what should be an object and what should not. If you do and use objects whenever you are not sure a plain old datastructure is enough, then you should be fine. And if you find out later that something should have been an object, then the fact whether you can attach a method or two will be the least of your worries. Sure, it might have been made easier to do so, but if something was designed and used for a long time as a plain, transparent data structure, then all the code accesses it as such. And does things you would not want code to do with the insides of an object. So you either refactor properly or end up with something that's treated neither as an object nor as a datastructure.

          Jenda
          Enoch was right!
          Enjoy the last years of Rome.

    Re: Benefits of everything is an object? Or new sigils?
    by SuicideJunkie (Vicar) on Mar 02, 2010 at 16:57 UTC

      A program should not be pushing and shifting a pseudo-global from hundreds of locations. Code duplication like that should be avoided by making a subroutine.
      Since you naturally put all those similar subroutines close to each other in the code, you can then tell when you've got a critical mass and it has become worth it to fission the code off into a "human resources" object or module.

      You don't need to refactor a hundred places. You only need to touch the subs, and they are all conveniently located in one place.


      PS: Personally, I always use refs unless I know for sure that the array or hash will never be passed around. Makes variables more consistent to use, and easy to pass around.

      I suppose that means that I *don't* use sigils to determine the type. Instead I use the variable name itself. Singular vs plural, noun vs adjective. As far as arrays vs hashes go, I have found that there is very little overlap between "order is important" and "needs to look up specific entries by name". On the rare occasions where both are needed, it is complex enough to be worth an object and then it supports both so there is no problem.

      PPS: Hopefully there is no confusion between "you can do everything as an object" vs choking TIMTOWDI by making it "you must do everything as an object".
      The latter would be bad.

    Re: Benefits of everything is an object? Or new sigils?
    by james2vegas (Chaplain) on Mar 04, 2010 at 16:25 UTC
      There are a number of modules for converting hashes to blessed hashrefs with accessors for the elements within, f.e. Data::AsObject and Hash::AutoHash. In this code I imagined you'd be using an %employees hash rather than an array, and keyed it on some imaginary employee id. I then subclassed Data::AsObject::Hash to add a new method to take a reference to the employees hash and create the accessors (through the dao function) automatically.

      I was able to add custom methods to my EmployeeData class which can work on $self as either a subclass of Data::AsObject::Hash or as a hashref (or hash, through dereferencing) and you can continue to use the %employees hash as before.

      If you wished to use an array, then dao would need to be given a hashref with one key and a reference to the array as the value, e.g. dao { data => \@employees }.

      Of course, you could just as easily have created a completely new class, which just blessed \@employees (or \%employees) into $class and just used it as a means to add methods.

      If you keep both %employees and $employees in the same scope it would provide a (really ugly) way of keeping the original array|hash and its blessed reference and using, f.e., both push @employees, { name => 'Foo Bar' }; and $employees->method_call; in the same code, though hopefully at some point you'd switch over to just the object/reference.

      I don't this is some 'problem' which requires the use of slow modules like autobox or some wholesale re-invention of Perl symbology. Just some reasonable amount of pre-planning and willingness to make reasonable, incremental changes in existing code.

        As far as I understand these packages simplify the workaround I sketched...

        > If you keep both %employees and $employees in the same scope it would provide a (really ugly) way of keeping the original array|hash and its blessed reference

        yeah indeed ugly, thats what I meant.

        > I don't this is some 'problem' which requires the use of slow modules like autobox ...

        Contrary to the believe of the group that flames against me, I'm not propagating autobox as an existing solution! (I was asking a question in another thread about autobox and hit the nerve of a group of united fanboys. But I'm tired to repeat this!)

        I was talking about the theoretical benefits JS has over Perl in this respect, because there a hash acn be treated as object right from the beginning.

        Tieing this hash provides the same functionality in Perl like altering the object in JS (Phase 2)

        BTW: According to the docs, autobox isn't slower than method calls on blessed references. >or some wholesale re-invention of Perl symbology.

        A Sigil for each Ref-Type would open a lot of simplifications for problems newbies face when starting to learn Perl.

        Believe it or not, many people stop and hate Perl just because of these problems...long before even entering a level allowing to understand Data::AsObject and Hash::AutoHash.

        Many thanx for your contribution, it's one of the better ones lifting the level of this discussion! :)

        Cheers Rolf

          In a way you could say there is a sigil for each ref-type, when it gets deferenced (or when it is actually used as the base type, and not a reference in a scalar), @ => @$, % => %$, & => &$.

          For amusement or other purpose, here is code that lets you use a hashref or hash (or arrayref or array) by blessing the former (and the ref of the latter) into an (initially) non-existent package, and then create a function in that package. The function can be called in the usual way ($blessedref->function), but can also be called with NameSpace::function(%indirectlyblessedhash) owing to the \% prototype on the function which uses the reference of that parameter.

          If you use references for your data structures instead of plain hashes and arrays, and bless those references into appropriate packages (and dereference where needed), you will have the equivalent functionality to javascript array|objects. For me, at least, the requirement (softened by autobox), that objects be scalars makes sense, as scalars are for holding one value (base type or object) at a time.


    Re: Benefits of everything is an object? Or new sigils?
    by Xiong (Hermit) on Mar 06, 2010 at 10:03 UTC

      Upvoted the OP, as it's an interesting meditation; but I disagree with most of it. In the Bad Old Days, if you found out you needed a 12-column string variable after you'd allocated 10 bytes of storage, you were toast.

      Planning your data structures must come early in any project. I feel that if you find you need to revise extensively your data structures, you need to reconsider your entire approach. If it's just a matter of hanging one bag onto the existing structure then it's likely just writing another subroutine. If it's more than that, then it may well be time to throw out a lot of code and start over. I don't say that can't be painful.

      That's why I spend so much time sketching data structures on paper before I touch the keyboard. By no means do I avoid all later redesign; but I try to cut down on the number of slash-and-burn incidents. When I fail, I refactor. Perl makes it very easy to minimize the damage from poor planning but I don't want to lacquer any real mistakes into the project's frame.

      I don't hold with making every variable an object; that's fanaticism when carried to an extreme. (Here is a somewhat fanatic criticism.) I do use objects when they make sense: when I'm passing something persistent around, or working on it in many scopes, and especially when there's a natural hierarchy of classes in the data. I avoid creating verb or adjective objects; they don't make sense.

      OP's point about sigils is well taken. One reason to avoid too many objects is to keep all variables from looking alike. But I'd argue against any change in syntax, any additional sigils. I don't rely on sigils to tell me what a variable contains; I expect them to tell me in what context an expression is evaluated. I do not usually employ both $foo and @foo in one place, or even one project. I don't welcome the Perl6 syntax in which %foo{3,4} is a hash slice.

      I would also argue that one ought not use blessed array refs as objects. Traditional (if I may use the word) Perl objects are blessed hashrefs, with it being understood that these may well point to HoH, HoA, or more complex structures. More modern objects (again, if I may be excused) are the flyweight (inside-out) kind. While any code might use an older module (with traditional objects) but create modern objects, I'd hope that all objects created in a given project be of the same kind, one or the other.

      We should not cavil at contrived examples; they're often better in discussion than the whole nasty real thing. But I have a difficult time with a plural object. When I'm in doubt, I try it in English: "Have an @employees"; "My @employees is an object in class ____". Maybe not so much. Calling an object, a data structure which is basically an array, violates the common understanding that an object is a thing belonging to a class and to which attributes pertain. A glass may contain many marbles but those marbles are but one attribute of the glass. A $roster may be an object; $roster->get_names_of($active) may return an array -- possibly of objects. Nor is there any objection to assigning that to the small-scoped @employees.

      Conway is not generally in favor of bumps but if you feel you need to clarify, I won't object to $roster_obj or _ref. If you really do feel you need a blessed array ref, I'd like to see $employees_aryobj; it's fair warning.

      On the same page, I'd like to mention an old guy who debugged all code by placing a sheet of paper on the right side, covering the comments. He wasn't interested in what the author said the code would do. Again, I expect the sigils only to tell me what will happen; not what the author's intent may have been. For the latter (since I'm not that good), I'll read the comments.

      I intend no offense to say that the example given wants to have OO cake and eat it as well. I would not try to push() anything into an object. If I needed to do that, I'd $roster->push($new_guy); or better, $roster->hire($new_guy) or perhaps $new_guy->hire($roster).

      If all you have is object orientation then everything looks like an object.

        Thanks for this reply, it deepens the meditation into new dimensions worth at least one new thread. :-)

        Unfortunately I can't spend as much time to reply here like I did for other posts in this thread.

        > That's why I spend so much time sketching data structures on paper before I touch the keyboard.

        So do I, but sometimes its better to just start to code with the intention to prototype the real thing. And even in prototyping the Javascript approach is less painful than Perl.

        > I would also argue that one ought not use blessed array refs as objects.

        I disagree, maybe I'm too influenced by Conways OOP book but I see Perls possibility to bless any ref as the advantage of this (maybe too?) flexible OO System.

        After tieing the array to a new class, push() will act however you want it to act, e.g. the data can be easily inside out. I can't see why a blessed arr_ref should act differently to a blessed hash_ref, so no need to worry about ugly side effects.

        > But I have a difficult time with a plural object.

        IMHO this "plural naming convention" is a workaround for missing sigils. Actually I spend too much time to invent clear plural names where I would prefer to simply set a sigil.

        Normally I have my own hungarian notation writing something like $A_employees or $employees_ar to denote the nature of the variable. Contrary to PBP I prefer the former, because I want to know what I'm dealing with at one glance and _one_ end of the word , not on both ends. And Perl favors the beginning...

        > I expect the sigils only to tell me what will happen; not what the author's intent may have been. For the latter (since I'm not that good), I'll read the comments.

        I agree to a certain point, the sigils should be very clear about context and data type.

        I think mentioning Perl 6 was misleading...I'm also not happy about hash-slices which start with a $! :(

        In my vision these sigils should only be in scalars context, such that €employees == $employees is always true.

        The differences I'm meditating of are

        I see plenty of advantages in this approach...

        Cheers Rolf

        ¹) this is a contrived example, I know that a hash with key "phonenumbers" is a better choice than using an array with an index $idx_phonenumbers! :)

        ²) there's another possibility to be mentioned, IIRC Perl 6 allows a slice to be called with "method syntax"  ->@[LIST] (of course in Perl6 with a point and not an arrow ;)

    Log In?
    Username:
    Password:

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

    How do I use this?Last hourOther CB clients
    Other Users?
    Others avoiding work at the Monastery: (6)
    As of 2024-04-23 09:32 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      No recent polls found