Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can Raku OO help me avoid integration layer boilerplate

Tags:

oop

raku

I so enjoy the low boilerplate of raku OO that I am a little surprised that I cannot shake off some integration layer boilerplate.

Here is what I have working today (somewhat golfed):

class Error {
    has Real  $.absolute;

    method percent( $value ) { 
        "{ ( $!absolute / $value ).abs * 100 }%" 
    }   
}

class Measure {
    has Real  $.value;
    has Error $.error;

    method new( :$value, :$error ) { 
        self.bless( :$value, error => Error.new( absolute => $error ) ) 
    }   
    method error-percent {      # <==== why do I need this intermediary method??
        self.error.percent( $!value )
    }   
}

my $m = Measure.new( value => 10, error => 1 );
say $m.error-percent;   #10%

This works in that...

  • it separates concerns of Measure "container" and Error "element"
  • $.value rightly stays in the measure and is passed "up" when needed

BUT

It does NOT reflect the fact that many error operations will need the measure value. So (how) can Raku's cool OO schema help me put a "proxy" to the $.value in the Error class

I am aiming at something like this pseudo-code:

class Error {
    has Real     $.absolute;
    has Callable &!value-cb;

    method percent {
        "{ ( $!absolute / &!valuecb() ).abs * 100 }%" 
    }   
}

class Measure {
    has Real  $.value;
    has Error $.error;

    method new( :$value, :$error ) { 
        self.bless( :$value, error => Error.new( absolute => $error ), value-cb => &.value() )
    }   
}

my $m = Measure.new( value => 10, error => 1 );
say $m.error.percent;   #10%    <=== note the difference, I just go straight there!

So the concept I am groping for is to create some kind of closure / function for the $.value and push it into an Error attribute on construction.

I am happy to stick with what I have, decorating most methods on the Error object with $.value if that is considered the safest route. And I apologise in advance for my lack of knowledge on the various OOP / pattern terminology for this trick.

like image 245
p6steve Avatar asked Sep 23 '21 18:09

p6steve


Video Answer


1 Answers

I'm not 100% sure I've grasped what you're trying to accomplish, so I'll start by paraphrasing what I think your goal is and then say how I'd solve that problem. Please correct me if I'm solving the wrong problem.

As I understand it, you want an Error object that can report an error both in absolute terms and as a percentage of the Measure's value. However, you don't want to take the straightforward approach of giving the Error object a field that records the associated Measure.value because that would create multiples sources of truth that you'd have to keep in sync. So you'd like to have a way for the Error to access the Measure.value without storing it separately. Is that about right?

If so, here's one approach. I'm not sure it's that much more concise than the code you posted above in this golfed example, but it avoids the need for any decorated methods on Measure. (The Measure.value in the code below is rw so that I can show how Error stays syncronized, but there's no other reason it needs to be rw.)

The basic idea is give the Error a $!measured-value field and then to bind that field to the associated Measure's $.value. Here's how that could look:

class Measure {...}
class Error  {
    trusts Measure;
    has Real  $.absolute;
    has Real  $!measured-value;

    method !bind-value(\value) { $!measured-value := value };

    method percent {
        "{ ( $!absolute / $!measured-value ).abs * 100 }%"
    }
}

class Measure {
    has Real  $.value is rw;
    has Error $.error;

    method new(:$value, :$error is copy) {
        $error = Error.new: absolute => $error;
        given self.bless: :$value, :$error {
            $error!Error::bind-value: .value;
            $_
        }
    }
}

my $m = Measure.new( value => 10, error => 1 );
say $m.error.percent;   # OUTPUT: «10%»
$m.value = 20;
say $m.error.percent;   # OUTPUT: «5%»

Would that do what you want?


(Also, bonus thought unrelated to the question you asked: percent strikes me as a perfect use case for an allomorph, which would let users of the code have access to the percentage either as a nicely formatted Str or an a numerical value that they can do more math with. So, something like

do given ( $!absolute / $!measured-value ).abs { RatStr.new: $_, "{$_ × 100}%"}

I realize that's an aside, but it's too much fun not to mention!)

like image 163
codesections Avatar answered Oct 08 '22 14:10

codesections