Skip to content

Commit

Permalink
Let Array and Hash be smarter about parameterizing subclasses
Browse files Browse the repository at this point in the history
First of all, if re-parameterizing a previously parameterized type
fall back to the firs non-mixin parent in MRO. This would make
`class A is Array[Int] {}; say A[Str:D];` produce an `A[Str:D]`, not
`Array[Int][Str:D]` as it used to be. `Array[Int].^parameterize(Str:D)`
would also become `Array[Str:D]`.

Note that methods defined in `Array::Types`, `Hash::Object`, and
`Hash::Typed` roles would still override subclass' methods. I.e. it is
not possible for the class `A` above to override method `new` or
`BIND-POS` because `Array::Typed` has them. A dev must take special care
if they want their own versions of these.
  • Loading branch information
vrurg committed Dec 16, 2023
1 parent c22439e commit 5f8cf56
Show file tree
Hide file tree
Showing 5 changed files with 17 additions and 12 deletions.
5 changes: 3 additions & 2 deletions src/core.c/Array.rakumod
Original file line number Diff line number Diff line change
Expand Up @@ -1461,11 +1461,12 @@ my class Array { # declared in BOOTSTRAP
!! self.clone
}

method ^parameterize(Mu:U \arr, Mu \of) {
method ^parameterize(Mu:U \type, Mu \of) {
if nqp::isconcrete(of) {
die "Can not parameterize {arr.^name} with {of.raku}"
die "Can not parameterize {type.^name} with {of.raku}"
}
else {
my \arr = type.^mro.first(!*.^is_mixin);
my $what := arr.^mixin(Array::Typed[of]);
# needs to be done in COMPOSE phaser when that works
$what.^set_name("{arr.^name}[{of.^name}]");
Expand Down
4 changes: 2 additions & 2 deletions src/core.c/Array/Typed.rakumod
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,12 @@ my role Array::Typed[::TValue] does Positional[TValue] {
method is-generic { nqp::hllbool(callsame() || nqp::istrue(TValue.^archetypes.generic)) }

multi method INSTANTIATE-GENERIC(::?CLASS:U: TypeEnv:D \type-environment) is raw {
Array.^parameterize: type-environment.instantiate(TValue)
self.WHAT.^parameterize: type-environment.instantiate(TValue)
}
multi method INSTANTIATE-GENERIC(::?CLASS:D: TypeEnv:D \type-environment) is raw {
my Mu $descr := type-environment.instantiate(nqp::getattr(self, Array, '$!descriptor'));
nqp::p6bindattrinvres(
Array.^parameterize(type-environment.instantiate(TValue)).new( |(self.elems ?? self !! Empty) ),
self.WHAT.^parameterize(type-environment.instantiate(TValue)).new( |(self.elems ?? self !! Empty) ),
Array, '$!descriptor', $descr )
}

Expand Down
10 changes: 6 additions & 4 deletions src/core.c/Hash.rakumod
Original file line number Diff line number Diff line change
Expand Up @@ -444,16 +444,18 @@ my class Hash { # declared in BOOTSTRAP
!! self.clone
}

method ^parameterize(Mu:U \hash, Mu \of, Mu \keyof = Str(Any)) {
method ^parameterize(Mu:U \type, Mu \of, Mu \keyof = Str(Any)) {

my \hash = type.^mro.first(!*.^is_mixin);

# fast path
if nqp::eqaddr(of,Mu) && nqp::eqaddr(keyof,Str(Any)) {
hash
type
}

# error checking
elsif nqp::isconcrete(of) {
"Can not parameterize {hash.^name} with {of.raku}"
"Can not parameterize {type.^name} with {of.raku}"
}

# only constraint on type
Expand All @@ -467,7 +469,7 @@ my class Hash { # declared in BOOTSTRAP

# error checking
elsif nqp::isconcrete(keyof) {
"Can not parameterize {hash.^name} with {keyof.raku}"
"Can not parameterize {type.^name} with {keyof.raku}"
}

# no support for native types yet
Expand Down
6 changes: 4 additions & 2 deletions src/core.c/Hash/Object.rakumod
Original file line number Diff line number Diff line change
Expand Up @@ -267,13 +267,15 @@ my role Hash::Object[::TValue, ::TKey] does Associative[TValue] {
}

multi method INSTANTIATE-GENERIC(::?CLASS:U: TypeEnv:D \type-environment --> Associative) is raw {
Hash.^parameterize: type-environment.instantiate(TValue), type-environment.instantiate(TKey)
self.WHAT.^parameterize: type-environment.instantiate(TValue), type-environment.instantiate(TKey)
}

multi method INSTANTIATE-GENERIC(::?CLASS:D: TypeEnv:D \type-environment --> Associative) is raw {
my Mu $descr := type-environment.instantiate( nqp::getattr(self, Hash, '$!descriptor') );
nqp::p6bindattrinvres(
Hash.^parameterize( type-environment.instantiate(TValue), type-environment.instantiate(TKey) ).new(self),
self.WHAT.^parameterize(
type-environment.instantiate(TValue),
type-environment.instantiate(TKey) ).new(self),
Hash, '$!descriptor', $descr )
}

Expand Down
4 changes: 2 additions & 2 deletions src/core.c/Hash/Typed.rakumod
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ my role Hash::Typed[::TValue] does Associative[TValue] {
}

multi method INSTANTIATE-GENERIC(::?CLASS:U: TypeEnv:D \type-environment --> Associative) is raw {
Hash.^parameterize: type-environment.instantiate(TValue)
self.WHAT.^parameterize: type-environment.instantiate(TValue)
}
multi method INSTANTIATE-GENERIC(::?CLASS:D: TypeEnv:D \type-environment --> Associative) is raw {
my Mu $descr := type-environment.instantiate( nqp::getattr(self, Hash, '$!descriptor') );
nqp::p6bindattrinvres(
Hash.^parameterize( type-environment.instantiate(TValue) ).new(self), Hash, '$!descriptor', $descr )
self.WHAT.^parameterize( type-environment.instantiate(TValue) ).new(self), Hash, '$!descriptor', $descr )
}

multi method raku(::?CLASS:D \SELF:) {
Expand Down

0 comments on commit 5f8cf56

Please sign in to comment.