Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to (implicitly) drop a Raku role mixin?

this new question is a follow up to my previous that has emerged as I flesh things out. Please note that I have also done some research and I am consciously skirting the Scalar Mixins bug mentioned here. So I am mixing the role in to the Object and not to the Scalar container.

Big picture is to do math operations that also perform simple error calculations.

Here is a concise version of my failing code:

  1 role Error {       
  2     has $.abs-error 
  3 }   
  4 
  5 multi prefix:<-> ( Error:D $x ) is default {
  6     # - $x;                             # fails - enters an infinite loop 
  7     # - $x.Real;                        # fails - does not drop the Error mixin
  8     ( 0 - $x ) does Error($x.abs-error) # works - but relies on the infix:<-> form
  9 }
 10 
 11 my $dog = 12.5 does Error(0.5);
 12 
 13 #what i have...
 14 say $dog;               #12.5
 15 say $dog.WHAT;          #(Rat+{Error})
 16 say $dog.abs-error;     #0.5
 17 
 18 #what i want...
 19 say (-$dog);            #-12.5
 20 say (-$dog).WHAT;       #(Rat+{Error})
 21 say (-$dog).abs-error;  #0.5

The heart of my question is:

  • as a user of $dog I can get at the value of the variable (12.5) on line 14
  • sooo how can I get the unadorned value somewhere around line 7?

I have tried (desperately?) a few things:

  • coercion to Real (still get the mixed in object)
  • assignment to Real container (that permits Rat+{Error} ~~ Real)
  • $dog.default => No such method 'default' for invocant of type 'Rat+{Error}'

Thanks for all advice!!

like image 780
p6steve Avatar asked Sep 08 '21 10:09

p6steve


2 Answers

The direct answer to the question posed: no, there's no operation to undo a mixin. You can do some tricks to reach the functionality of the original type, however:

  • In the case of a method override, use the $obj-with-mixin.OriginalType::method-name() form to call methods that have been overridden.
  • In the case of a multi sub (such as the operators), you could do &prefix:<->.cando(\(1.5)).head to resolve, but not call, the implementation of - on Rat, and then call it directly.

Looking at this question and your previous one, however, it seems you're fighting the language every step of the way; is default is really a last resort, and even if you can get it to work using the mixin approach, you'll find the result is terribly slow, in no small part because mixins trigger deoptimization (falling out of specialized and JIT-compiled code back to the interpreter).

Perhaps explore a design using composition instead:

# An object holding the value and the error.
class Error does Real {
    has Real $.value;
    has Real $.abs-error;
    multi method Real(Error:D:) { $!value }
    multi method gist(Error:D:) { "$!value±$!abs-error" }
}

# A cute constructor of this type, just for fun.
multi infix:<±>(Real $value, Real $abs-error) {
    Error.new(:$value, :$abs-error)
}

# Negation; no `is default` or other tricks required!
multi prefix:<->(Error $e --> Error) {
    Error.new(value => -$e.value, abs-error => $e.abs-error)
}

# It works!
my $x = 4.5 ± 0.1;
say $x;
say -$x;
like image 86
Jonathan Worthington Avatar answered Oct 23 '22 19:10

Jonathan Worthington


Coming off @raiphs comment, I have figured out a quick and dirty fix that uses the fact that I know the .say method works to produce the unadorned value of the Object...

... OO programming purists, please look away now.

  1 role Error {       
  2     has $.abs-error;
  3     
  4     method negate {
  5         my $val = +"{self}";     #extract unadorned value of $x
  6         (- $val) does Error( $!abs-error );
  7     }   
  8 }   
  9 
 10 multi prefix:<-> ( Error:D $x ) is default { $x.negate }
 11 
 12 my $dog = 12.5 does Error(0.5);
 13 
 14 #what i get...
 15 say (-$dog);            #-12.5
 16 say (-$dog).WHAT;       #(Rat+{Error})
 17 say (-$dog).abs-error;  #0.5
like image 22
p6steve Avatar answered Oct 23 '22 17:10

p6steve