The problem
45.AngleCosine
In standard Raku, syntax of the form foo.bar denotes a "method call".
Slightly simplifying, this means Rakudo should:
Presume there's a routine corresponding to AngleCosine.
Discover candidate routines corresponding to the name AngleCosine (and sort them into order, with the fittest candidates listed first).
Try binding to routine candidates in order of their fitness, and as soon as one successfully binds, call it and consider the call resolved.
Fail the resolution attempt if no candidates successfully bound, throwing a "No such method" exception.
The candidate discovery step is driven by the syntactic form of bar in foo.bar:
If bar starts with a letter (or underscore), Rakudo will start looking for methods associated with foo. In your example, bar is AngleCosine and foo is 45, so the search will start by looking for methods named AngleCosine in the Int class (45 is an instance of the Int class).
When searching for methods associated with an object, Rakudo will look at each of the classes included in the object's "Method Resolution Order" to discover candidates. In your example, foo is 45, so the MRO is as listed by 45.^mro. This displays ((Int) (Cool) (Any) (Mu)).
In standard Raku there is no method named AngleCosine declared in any of the classes Int, Cool, Any, or Mu. Thus Raku displays the error message:
No such method 'AngleCosine' for invocant of type 'Int'
Solutions
Stick with the precise syntax 45.AngleCosine. This is fraught and complicated, and will be discussed last in this answer.
Use a non-method call solution. For examples, see answers by @p6steve and @ValleLukas.
Use a suitably initialized variable (eg $x) on the left of the method call . instead of a value literal (eg 45). See the Solutions that change the foo of foo.bar section below.
Use a suitably altered mention of the routine (eg &AngleCosine) on the right of the method call .. See the Solutions that change the bar of foo.bar section below.
Solutions that change the foo of foo.bar
You could change the declaration of your Angle class and then instantiate and use an Angle instance like so:
class Angle {
has Numeric $.degrees;
method AngleCosine { 'do something with $!degrees' }
}
my Angle $x .= new: degrees => 45;
say $x.AngleCosine; # 'do something with $!degrees'
But you've said you want a solution "without the .new stuff".
Another option is using a coercion type.
Until recently you could only use coercion types with routine calls. But with Rakudo versions from the last month or so something like the following should work:
class Angle {
has Numeric $.degrees;
method COERCE ( Numeric $degrees ) { self.new: :$degrees }
method AngleCosine { 'do something with $!degrees' }
}
my Angle() $x = 45;
say $x.AngleCosine; # do something with $!degrees
Solutions that change the bar of foo.bar
If you were willing to abandon all of OO except for the lhs.rhs syntax, you could write:
sub AngleCosine ($_) { 'do something with $_' }
say 45.&AngleCosine; # do something with $_
This works because the prepended & directs Raku to presume the routine that follows it:
Is not a method, but instead a subroutine that is to be treated as if it were a method, passing the value on the LHS of the . as the routine's first argument;
Should not be discovered by searching classes;
Should, if the first character after the & is a letter (or underscore) -- which is indeed the case with &AngleCosine -- be discovered by looking in the lexical subroutine namespace.
If you wanted to move back toward OO by declaring an actual method, but still keep it outside of any class, you could write:
my method AngleCosine { 'do something with $_' }
say 45.&AngleCosine; # do something with $_
The first line, declaring a method outside of a class construct, and with a my, installs the method in the exact same namespace as non-method subroutines, so the calling syntax and effect is the same as the example just explained above.
If you wanted to stick with having an AngleCosine method in your Angle class, you can use the above technique of using a prepended & to switch off the automatic class based method resolution, but then directly inline a block that manually dispatches to the method declared in your Angle class:
class Angle {
method AngleCosine { 'do something with self' }
}
say 45.&{ Angle.^find_method('AngleCosine')[0]($_) }
Hopefully that is suitably horrific looking code that you are never tempted to even think you have to go this route, and will forgive me for not explaining it further.
Solutions using the precise syntactic form 45.AngleCosine
So finally we arrive at solutions that exactly fit what you suggested you really wanted.
First, you can always change any aspect of Raku's syntax or semantics via slangs. But for most problems, doing so is like using a nuclear power station to power a light bulb. I'm not going to cover how a slang might solve this problem because it would be essentially absurd to do so.
Which leaves one last approach: monkey patching, or more specifically "monkey typing". Hopefully you get the picture from the name; it means monkeying around with types, and is generally a very dodgy thing to do, a technique that will mess things up if you don't know exactly what you're doing, and maybe even then.
Raku will let you monkey with typing, despite it being dodgy, rather than simply banning it. But to protect the innocent, Raku insists that you make it absolutely clear that you really want to do this dodgy thing. So you have to use a LOUD pragma (either the specific use MONKEY-TYPING; or the general use MONKEY;).
With that preamble out of the way, here's the code:
role Angle { method AngleCosine { 'type has been monkey patched' } }
use MONKEY-TYPING;
augment class Int does Angle {}
say 45.AngleCosine; # type has been monkey patched
This code augments the Int class to add an AngleCosine method.
Note that you can't add new attributes or parent classes to Int. Once a class has been composed, which for an ordinary class declaration occurs immediately after the } at the end of the class declaration has been encountered, its list of attributes and parents is permanently fixed.
So this approach can't be used to inject your $.degrees attribute, and you have to put the method in a role instead of a class. (A role is mostly like a class but is generally more versatile, and this is an example of when you can do something if you declare a bunch of methods and/or attributes with the keyword role, but not if you use the keyword class.)
Not only is this solution unable to add an attribute or parent class, but it's still extremely inappropriate for most use cases due to other serious problems. In a sense it's even worse than solving your problem with a slang, because what I've shown will barely power a light bulb, and, much worse, might well melt down.
But it's vastly simpler than writing a slang, at least for very simple cases, and if you don't care about a potential melt down, maybe, just maybe, it's the right thing. In particular, it can make sense for code that hopefully works most of the time but is allowed to occasionally fail or do strange things for strange reasons. You've said your context is testing, and if you mean one off testing, then perhaps monkey typing is good enough.
Why the Rat:D type constraint didn't do what you wanted
This is a "bonus" section related to your original question which I'll repeat here in abbreviated form:
I am trying to write a test method to print to screen the cosine of an angle given in degrees.
class Angle {
method AngleCosine( Rat:D: --> Rat:D ) { ... }
}
say (45.0).AngleCosine
No such method 'AngleCosine' for invocant of type 'Rat'
in block <unit> at <unknown file> line 1
What am I doing wrong?
You might reasonably ask why explicitly specifying the invocant type Rat:D in your AngleCosine method didn't do what you intended.
As discussed in the previous Solutions using the precise syntactic form 45.AngleCosine section, this is known as "monkey typing" and it's a dodgy thing to do. So while Raku will let you monkey with the typing, it insists that you make it absolutely clear that you want to do so by using a MONKEY pragma.
You might then still reasonably ask why Rakudo didn't produce a helpful error message explaining that it won't work, and what to do about it (eg "Perhaps you want the MONKEY pragma?").
And that is because it can be useful to explicitly specify an invocant type. So Rakudo doesn't complain about code just because it explicitly specifies an invocant type. For example, if you don't directly contradict the enclosing class, but merely refine the type, eg to be a sub-class, then the method will work when the invocant accords with the refinement, which adds nuance to the binder stage described in an earlier section. And that can be useful. So there's no need for an error message in that scenario.
Which leaves us with the question why Rakudo doesn't display an error when the explicitly specified invocant class directly contradicts the enclosing class, as it did in your case? Why didn't it just guess you meant to monkey patch, and provide guidance on what you can do?
And that's because it can't know for sure that your contradiction of the enclosing class is inappropriate. Sometimes it is.