in reply to Re^14: Assignable Subroutines
in thread Assignable Subroutines

For now, I'll provide two other ways to accomplish what you want, through functional programming or just using Perl itself . . .

Both examples require more functionality in the code higher up. It's not OO due to insufficient encapsulation.

Again, if you don't want to use OO, that's fine, as there are plenty of places where OO is inappropriate. But if you are going to use it, do it right.

This is completely wrong. Now the object designer has to anticipate all of the infinte set of incorrect things that people might try to do with his object and explicitly make each one an error.

No, you don't:

package Employee; . . . sub apply_effect { my ($self, $effects) = @_; # $effects->list returns a list of objects containing # the singular effects foreach my $effect ( $effects->list ) { my $attribute = $effect->attribute; throw_exception() unless exists $self->{$attribute}; # Rest of code } }

Essentially a "deny by default" strategy. Now your test suite just needs to throw a few effects that are known to have attributes it's not going to use, check that exceptions were thrown, and you're set.

It really seems to me that in your game, buildings should just ignore the speed message. But even if your RTS engine is a special case where all sorts of things should ignore all sorts of messages then maybe you're right to do what you're doing but that's still not an argument as to why software in general should do it too.

The important point is that it's going to depend on the application. I was making a general framework, and in certain games it's going to make sense to ignore, while others might want to throw an error. It's none of my businesses to impose that on the game designer. Further, this strategy may be useful outside games, and I'd rather have a general framework that is easily modified rather than writing from scratch.

I can see the argument for using (improved) lvalue subs for internal use (as dragonchild mentioned higher up the thread). But rarely/never outside the class.

"There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.

Replies are listed 'Best First'.
Re^16: Assignable Subroutines
by fergal (Chaplain) on Jan 27, 2005 at 21:10 UTC
    Both examples require more functionality in the code higher up. It's not OO due to insufficient encapsulation.
    I don't understand what you mean by "higher up". The code is almost identical to yours, just written directly in Perl, rather than some new and cumbersome operator application language.

    I also don't understand what you mean about "insufficient encapsulation". Why is that? If you mean that I shouldn't be calling $_->salary directly than you are arguing circularly as this whole discussion is about whether that's OK or not. If you don't mean that, please tell me what I should have encapsulated but didn't.

    As for the "deny by default" strategy, I get it free with Perl already, why do I need to write all that code to get it back again? Plus, allow by default is really easy to implement in "normal" Perl too so it's not like it was urgently in need of simplification. Also, it's a pain now to ignore some but not others and it's still under the control of the called object, not the caller. This may suit a framework (where things kind of become inside-out) but it's not the general case.

      I don't understand what you mean by "higher up".

      I mean the code to handle this is lifted outside the class and into the caller.

      The code is almost identical to yours, just written directly in Perl, rather than some new and cumbersome operator application language.

      I, for one, like the addition of small, domain specific languages. Your choice is to either learn an API or learn a (small) language. Choosing one or the other tends to force you twards different design decisions, but the cost of learning is not so different.

      I also don't understand what you mean about "insufficient encapsulation".

      Encapsulation is hiding data and behavior. Applying trvial mutators exposes data. It's therefore bad encapsulation. QED.

      But again, I think dragonchild's giveRaise() and givePromotion() methods are better than my own implementation.

      "There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.

        I don't understand what you mean by "higher up".
        I mean the code to handle this is lifted outside the class and into the caller.
        It's in the caller in your code too, it's just all stuffed into an effect object which is created and applied by the caller and I still haven't seen an example of why that might cause a problem.
        I, for one, like the addition of small, domain specific languages. Your choice is to either learn an API or learn a (small) language. Choosing one or the other tends to force you twards different design decisions, but the cost of learning is not so different.
        Yes they're great but I have 2 problems with your.
        • It's domain is already completely covered by Perl
        • Your language has to be typed in as a structure that represents a parsed syntax tree which is not so convient
        If you want to continue use it fine but I really recommend looking at Tangram, the techniques in there would allow you to replace
        my $effect = Effect->new( [ salary => 1.02, '*' ], [ frobnitz => 1.5, '+' ], ); $_->apply_effect( $effect ) for @employees;
        with
        my $effect = Effect->new; $effect->salary *= 1.02; $effect->frobnitz += 1.5; $_->apply_effect( $effect ) for @employees;
        which may not look so different but because it's actual Perl it requires no learning at all. It also would allow stuff like
        $effect->salary = $effect->frobnitz + 1.5;
        Encapsulation is hiding data and behavior. Applying trvial mutators exposes data. It's therefore bad encapsulation. QED.

        That's exactly the circular logic I was talking about. Your using your own definition of encapsulation to to justify itself. You've basically said "it's not my kind of encapsulation therefore it's bad encapsulation".

        For me, encapsulation does not mean hiding all data it just means hiding particular pieces of data from particular parts of the program. In many situations, some data really does need to be exposed but by exposing the data through methods you are not breaking encapsulation because the actual internal data is still guarded from direct manipulation.

        You keep talking about trivial mutators. Are non-trivial ones OK? If so what should I do if I change my implementation so that a non-trivial mutator is now trivial?