dolmen
dolmen

Reputation: 8706

Binding with type coercion

I'm trying to use type coercion in a signature.

How can I fix the my Chars(Str) @a := 'hello'; line to make this code work?

class Chars is Array {
        submethod new(Str:D $s) {
                nextwith(|$s.comb);
        }
}

use MONKEY-TYPING;
augment class Str {
    method Chars { Chars.new(self) }
}


say Chars.new("hello").raku;
say "hello".Chars.raku;

my Chars(Str) @a := 'hello';

@a.raku.say;

Upvotes: 3

Views: 137

Answers (1)

raiph
raiph

Reputation: 32404

You haven't said what you're trying to do as a practical matter, so this answer attempts to read between the lines. It could luckily be close to what you want, or at least educational, or miles off. Either way, if you respond to this answer by commenting, and/or editing your answer, I might be able to make it more useful.

My best guess at the sorts of things you're after

The following seems to work OK, but no warranty is implied:

subset Char of Str where .chars <= 1;
say Char ~~ Str;         # True

class Chars is Array[Char] { multi method new (Str:D $_) { samewith |.comb } }

my @a is Chars;

say @a.WHAT;             # Chars

@a = Chars.new: 'foo';
say @a;                  # [f o o]
say @a.elems;            # 3
say @a.of;               # Char

@a = 'buzz'.comb;
say @a;                  # [b u z z]
say @a.elems;            # 4

@a = '4', '2';
say @a;                  # [4 2]

@a[3] := '9';
say @a;                  # [4 2 (Char) 9]

@a[4] = '100';           # Type check failed in assignment to ;
                         # expected Char but got Str ("100")
use MONKEY-TYPING;
augment class Str { method Chars { Chars.new: self } }

my (Chars(Str) $a) := \'hello';
say WHAT $a;             # Str
say $a;                  # [h e l l o]
$a[1..3] = 'bar'.comb;
say $a;                  # [h b a r o]
  • I introduced a Char subset to correspond to a single character string type constraint and used that constraint in is Array[Char] when declaring/defining the Chars class.

  • I used samewith, not nextwith. You'll see why if you change it back and run my code.

  • The type check error message is odd: "in assignment to ;".

  • my @a is Chars is a permanent and compile-time binding of a new Chars, and a corresponding type constraint, to the symbol @a.

Responding more literally to your question

Binding with type coercion

my (Chars(Str) $a) := '1'; say $a.WHAT; # (Chars)

I'm trying to use type coercion in a signature.

Using the word signature suggests either:

sub a (Chars(Str) $a) { $a }
say WHAT a '1'; # (Chars)

or, much less likely:

my Chars $a;
:(Chars(Str) $a) := \'hello';

The syntax :(...) specifies a standalone signature. They're rarely used and a bit of an odd duck. For example, if you bind to a standalone signature then any variables in it must exist in the immediate lexical scope in which the binding occurs. Like a routine signature, they are bound to a Capture. Unlike the routine signature binding that occurs when you call a routine, the compiler doesn't turn the RHS of the := binding (which are the equivalent of a routine's arguments) into a Capture for you, so you have to do so, eg by using the \ capture operator.

But I strongly doubt that's what you're after.


If you are really interested in declaring a variable outside a routine, then maybe use the list declaration syntax (even though you're only binding to one variable):

my (Chars(Str) $a) := \'hello';

The additional parens make the difference. But you still have to use binding and a capture.

And, weirdly (or maybe I'm just too sleepy?), if you try assignment, the coercion doesn't happen:

my (Chars(Str) $a) = \'hello';
say WHAT $a;                    # Str

How can I fix the my Chars(Str) @a := 'hello';?

If you try it you'll see an error message like: Coercion Foo(Bar) is insufficiently type-like to qualify a variable.

If you bind to a signature, you must bind a Capture.

You're declaring a variable with an @ sigil. That means a type constraint on its left constrains elements of the variable, not the overall variable itself. That is to say, a declaration like my Chars @a; declares an Array[Chars]. Given my code above that would be an Array[Array[Char]], an array of arrays of single character elements. I'm pretty sure you don't mean that.

Upvotes: 5

Related Questions