7

Another question might be "How do I inherit from builtin types?".

I have two questions really, but they are both from the same thing I'm playing with.

First, I can make a subset of a type when I want to constrain it further. I do that with MyInt which accepts anything that is an Int. I declare a variable to by MyInt and assign to it, but when I check its name, I get Int instead. So, what's up with that?

subset MyInt where * ~~ Int;

my MyInt $b = 137;
put 'Name: ', $b.^name;  # Int, but why not MyInt?

But, what I really wanted was a class named MyInt that does the same thing. I might want to add methods to

class MyInt is Int {}   # empty subclass

my MyInt $b = 137;
put 'Name: ', $b.^name;

This almost looks like it works, but I get an error:

Type check failed in assignment to $b; expected MyInt but got Int (137)

I understand what it's saying, but don't understand why I didn't get the same error when I used subset. That's question 1.5.

What I'd really like is the assignment of 137 to automatically turn itself into a MyInt when I assign it. I know that I can explicitly construct it, but it's a bit annoying that the parent class still turns it into an Int instead of using the type of the more derived type:

class MyInt is Int {}   # empty subclass

my MyInt $b = MyInt.new: 137;  # Still an Int
put 'Name: ', $b.^name;

I can override the new (taken directly from Int.pm), but I'm at a loss about change the type:

class MyInt is Int {
    method new ( $value --> MyInt ) {
        my $v = callsame; # let superclass construct it
        # But, how do I make it the more specific type?
        }
    }

my MyInt $b = MyInt.new: 137;  # Still an Int
put 'Name: ', $b.^name;

I can bless self, but that doesn't retain the value (and I didn't think it would and don't think it should. Looking at Int.pm, I can't see how it stores the value. It looks like it relies on a built-in type and might not be traditionally subclassable:

class MyInt is Int {
    method new ( $value --> MyInt ) {
        my $v = callsame; # let superclass construct it
        put "v is $v";
        # But, how do I make it the more specific type?
        # $v.bless (doesn't change the type, fails return type check)
        self.bless;  # doesn't retain value
        }
    }

my MyInt $b = MyInt.new: 137;  # Still an Int
put 'Name: ', $b.^name;
put 'Value: ', $b;  # 0

There is a rebless, but that isn't part of the chain of things avialable to Int or ClassHow:

class MyInt is Int {
    method new ( $value --> MyInt ) {
        my $v = callsame; # let superclass construct it
        put "v is $v";
        put "self is " ~ self.^name;
        put "HOW is " ~ self.HOW.^name;
        # No such method 'rebless' for invocant
        # $v.rebless: self.^name;
        $v.HOW.rebless: self.^name;
        }
    }

my MyInt $b = MyInt.new: 137;  # Still an Int
put 'Name: ', $b.^name;
put 'Value: ', $b;  # 0
brian d foy
  • 129,424
  • 31
  • 207
  • 592
  • 2
    1 and 1.5 is because `subset` doesn't create a new **type** in the sense of a subclass, but merely a named **type constraint**. As for how to properly create subclasses of core built-in types, that's something I'd like to know as well... :) The last time I experimented with that, I gave up and reverted to using composition instead of inheritance. – smls Jun 11 '17 at 20:55
  • there's also `nqp::box_i(42, MyInt)`, but this will only work for integers that fit into a native `int` (ie 64 bits) – Christoph Jun 12 '17 at 20:40
  • to get around the lack of nqp::box_I, we can use nqp::add_I or anything similar: `use nqp; class MyInt is Int { }; nqp::add_I(100000, 0, MyInt).^name.say` → `MyInt` – timotimo Jun 12 '17 at 20:45
  • @timotimo: nice idea, giving the short and sweet `method new(Int:D $value) { nqp::add_I(0, $value, self) }`; I tried to set `$!value` via `nqp::bindattr()`, but that segfaults oO – Christoph Jun 12 '17 at 20:55

1 Answers1

4

Here's a possible solution:

class MyInt is Int { };
my $x = 42;
Metamodel::Primitives.rebless: $x, MyInt;
dd $x;

Which produces:

MyInt $x = 42

There's probably a cleaner way of doing what you want, but I don't know what it is.

Important update See Raku rebless doesn't work with inhertited classes anymore.

raiph
  • 31,607
  • 3
  • 62
  • 111
BenGoldberg
  • 415
  • 3
  • 6
  • That's acting globally, E.g. `perl6 -e'class MyInt is Int { }; my $x = 42; Metamodel::Primitives.rebless( $x, MyInt); note 42.WHAT'`, outputs `(MyInt)` – dwarring Jun 11 '18 at 03:42
  • 1
    The `Metamodel::Primitives.rebless` call fails on raku 2019.11: `New type MyInt for Int is not a mixin type in block at line 1`. – Arne Sommer Jan 05 '20 at 18:07