8

I'm writing a class that looks like this:

class ListenSocket is Tap {
    has         $!VMIO;
    has Promise $.socket-host;
    has Promise $.socket-port;

    method new(&on-close, Mu :$VMIO, Promise :$socket-host, Promise :$socket-port) {
        self.bless: :&on-close, :$VMIO, :$socket-host, :$socket-port;
    }

    submethod BUILD(Mu :$!VMIO) { }
}

When I try to construct the class with defined Promises for $socket-host and $socket-port, their attributes in the class end up becoming undefined for whatever reason. What can I do to fix this?

Kaiepi
  • 3,230
  • 7
  • 27
  • Probably due to the empty method BUILD you have. `bless` actually calls it. If your definition is so straightforward, it's probably better if you don't define anything, or leave it to BUILD instead of defining `new` – jjmerelo Oct 08 '18 at 08:21
  • 1
    This probably works better if you change the name of `BUILD` to `TWEAK` – Elizabeth Mattijsen Oct 08 '18 at 08:59
  • Neither of these work since `&on-close` is a private member of `Tap` – Kaiepi Oct 08 '18 at 09:27

1 Answers1

7

I should have started off with what Liz wrote. Switch BUILD to TWEAK. Then the default BUILD will do its thing and your socket attributes will get correctly initialized.

The next problem is &on-close. See Lizmat's answer to Constructors in subclases showing how to deal with that if you can modify the superclass (Tap in this case) or Jonathan's authoritative answer to Inheriting private attributes in Perl 6 (which is about any access whatsoever to another class's attributes) showing you're out of luck if you can't modify the superclass.


Note that the above two issues aren't really about "private attributes" in a class making "public attributes" undefined. Nor are types relevant.

All attributes are technically private. The private/public distinction is about whether there's a public accessor for a private attribute.

Your custom BUILD is only initializing $!VMIO, the one that doesn't have a public accessor. You have neglected to initialize $!socket-host and $!socket-port, the attributes that do have public accessors (due to use of the public accessor twigil . when declaring them).

You presumably wrote a custom BUILD because the default BUILD only initializes attributes with public accessors. But if you do that you are taking on full responsibility for object construction and you must initialize all attributes that you want initialized.

It's better to write a TWEAK. Then you can just deal with the attributes without public accessors. A TWEAK just adds further initialization to the BUILD, which for the default BUILD is just initialization of the attributes with public accessors.

raiph
  • 31,607
  • 3
  • 62
  • 111