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

2

8

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!!

Lawana answered 8/9, 2021 at 10:36 Comment(0)
R
7

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;
Report answered 9/9, 2021 at 10:12 Comment(1)
thanks @jnthn - this makes excellent sense and I had kind of come to your view that mixins were going to be an uphill battle for this problem via another route (due to decidability of signatures on dyadic math operators when one is Error and t'other is Real) ... I hadn't thought of composing Real into the Error class ... will have a play with that!Lawana
L
4

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
Lawana answered 8/9, 2021 at 16:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.