I was curious about grammars being classes or singletons, so I created this small program to find out:
grammar Mini {
token TOP { \* <word> \* }
token word { \w+ }
}
proto sub is-class( | ) { * };
multi sub is-class( Grammar:D $g ) { return "Object" };
multi sub is-class( Grammar:U $g ) { return "Class" };
say is-class( Mini );
This uses multiple dispatch to find that out, and it turns out that Mini is actually a class. In general, would there be a shorter way of finding this out? Or a way that would not require to know the actual class of which the package might be an instance?
You can disambiguate 'instances' and 'classes' via DEFINITE, ie
Mini.DEFINITE ?? 'Object' !! 'Class'
or rather
Mini.DEFINITE ?? 'concrete object' !! 'type object'
should do the trick.
Related
I know that this will not work, since I tried it:
if $r ~~ BearingTrue || CompassAdj || CourseAdj { nextsame };
But - is there a neat, concise and legible way to do multiple Type smart matches on one line rather than have to expand to a given/when or if/else construct?
Have you tried :
if $r ~~ BearingTrue | CompassAdj | CourseAdj { nextsame };
This should give you an Any Junction that with then match OK.
This Answer is to let me address the comment from #jubilatious1 a bit more clearly.
I am working on a new raku module, see line 225 ish here Physics::Navigation
For illustrative purposes, the code now looks like this...
class BearingTrue { ...}
class BearingMag { ...}
sub err-msg { die "Can't mix BearingTrue and BearingMag for add/subtract!" }
class BearingTrue is Bearing is export {
multi method compass { <T> } #get compass
multi method compass( Str $_ ) { #set compass
die "BearingTrue compass must be <T>" unless $_ eq <T> }
method M { #coerce to BearingMag
my $nv = $.value + ( +$variation + +$deviation );
BearingMag.new( value => $nv, compass => <M> )
}
#| can't mix unless BearingMag
multi method add( BearingMag ) { err-msg }
multi method subtract( BearingMag ) { err-msg }
}
So I decided to recast the code to use multi-method add and subtract to check type matches to prevent a lost mariner from adding a magnetic bearing to a true one. I feel that this is cleaner even than Scimon's great answer, since in that instance, my method was accepting all child types of Bearing and then using an if statement to detect a type error.
You are welcome to go...
zef install https://github.com/p6steve/raku-Physics-Navigation.git
then follow the example at the top of bin/synopsis-navigation.raku to use the module and make the various classes available in your own code.
If you are just keen to see how the pieces fit then I suggest writing your own simple classes on similar lines and working through the examples in books such as ThinkRaku chapter 12. I recommend this for the clarity and level of information and it treats inheritance and roles equally.
I am confident that others will feel my code style is over-reliant on inheritance. I feel that since a magnetic-bearing is strictly a derived concept from a bearing-in-general that this is right for my code - but roles and composition is less restrictive and provides similar encapsulation with better maintainability.
In theory, and according to the documentation, you can use any argument for methods in grammar actions.
grammar G {
token TOP { \w+ }
}
class Action-Arg {
method TOP ($match) { $match.make: ~$match }
}
class Action {
method TOP ($/) { make ~$/ }
}
class Action-Fails {
method TOP ($match) { make ~$match }
}
say G.parse( "zipi", actions => Action-Arg );
say G.parse( "zape", actions => Action );
say G.parse( "pantuflo", actions => Action-Fails );
However, the two first versions work as expected. But the third one (which would be a direct translation of the second), fails with
Cannot bind attributes in a Nil type object
in method TOP at match-and-match.p6 line 19
in regex TOP at match-and-match.p6 line 7
in block <unit> at match-and-match.p6 line 24
There's probably some special syntax going on (in the sense of make being actually $/.make, probably), but I'd just like to clarify if this is according to spec or is a bug.
That is because the make subroutine is one of those rare cases in Rakudo where it actually tries to access the $/ variable from the scope it is called from. Which is also how it is documented:
The sub form operates on the current $/
(from the documentation)
I have a class Configuration that reads in environment variables:
class Configuration {
has $.config_string_a;
has $.config_string_b;
has Bool $.config_flag_c;
method new() {
sub assertHasEnv(Str $envVar) {
die "environment variable $envVar must exist" unless %*ENV{$envVar}:exists;
}
assertHasEnv('CONFIG_STRING_A');
assertHasEnv('CONFIG_STRING_B');
assertHasEnv('CONFIG_FLAG_C');
return self.bless(
config_string_a => %*ENV{'CONFIG_STRING_A'},
config_string_b => %*ENV{'CONFIG_STRING_B'},
config_flag_c => Bool(%*ENV{'CONFIG_FLAG_C'}),
);
}
}
my $config = Configuration.new;
say $config.config_string_a;
say $config.config_string_b;
say $config.config_flag_c;
Is there a more concise way to express this? For example, I am repeating the environment variable name in the check and the return value of the constructor.
I could easily see writing another, more generic class that encapsulates the necessary info for a config parameter:
class ConfigurationParameter {
has $.name;
has $.envVarName;
has Bool $.required;
method new (:$name, :$envVarName, :$required = True) {
return self.bless(:$name, :$envVarName, :$required);
}
}
Then rolling these into a List in the Configuration class. However, I don't know how to refactor the constructor in Configuration to accommodate this.
The most immediate change that comes to mind is to change new to be:
method new() {
sub env(Str $envVar) {
%*ENV{$envVar} // die "environment variable $envVar must exist"
}
return self.bless(
config_string_a => env('CONFIG_STRING_A'),
config_string_b => env('CONFIG_STRING_B'),
config_flag_c => Bool(env('CONFIG_FLAG_C')),
);
}
While // is a definedness check rather than an existence one, the only way an environment variable will be undefined is if it isn't set. That gets down to one mention of %*ENV and also of each environment variable.
If there's only a few, then I'd likely stop there, but the next bit of repetition that strikes me is the names of the attributes are just lowercase of the names of the environment variables, so we could eliminate that duplication too, at the cost of a little more complexity:
method new() {
multi env(Str $envVar) {
$envVar.lc => %*ENV{$envVar} // die "environment variable $envVar must exist"
}
multi env(Str $envVar, $type) {
.key => $type(.value) given env($envVar)
}
return self.bless(
|env('CONFIG_STRING_A'),
|env('CONFIG_STRING_B'),
|env('CONFIG_FLAG_C', Bool),
);
}
Now env returns a Pair, and | flattens it in to the argument list as if it's a named argument.
Finally, the "power tool" approach is to write a trait like this outside of the class:
multi trait_mod:<is>(Attribute $attr, :$from-env!) {
my $env-name = $attr.name.substr(2).uc;
$attr.set_build(-> | {
with %*ENV{$env-name} -> $value {
Any ~~ $attr.type ?? $value !! $attr.type()($value)
}
else {
die "environment variable $env-name must exist"
}
});
}
And then write the class as:
class Configuration {
has $.config_string_a is from-env;
has $.config_string_b is from-env;
has Bool $.config_flag_c is from-env;
}
Traits run at compile time, and can manipulate a declaration in various ways. This trait calculates the name of the environment variable based on the attribute name (attribute names are always like $!config_string_a, thus the substr). The set_build sets the code that will be run to initialize the attribute when the class is created. That gets passed various things that in our situation aren't important, so we ignore the arguments with |. The with is just like if defined, so this is the same approach as the // earlier. Finally, the Any ~~ $attr.type check asks if the parameter is constrained in some way, and if it is, performs a coercion (done by invoking the type with the value).
So I mentioned this in a comment but I figured it would be good as an actual answer. I figured this would be useful functionality for anyone building a Docker based system so took Jonanthan's example code, added some functionality for exporting Traits Elizabeth showed me and made Trait::Env
Usage is :
use Trait::Env;
class Configuration {
has $.config_string_a is env;
has $.config-string-b is env(:required);
has Bool $.config-flag-c is env is default(True);
}
The :required flag turns on die if not found. And it plays nicely with the is default trait. Attribute names are upper cased and - is replaced with _ before checking %*ENV.
I have a couple of planned changes, make it throw a named Exception rather than just die and handle Boolean's a bit better. As %*ENV is Strings having a Boolean False is a bit of a pain.
I'm about to choose what language to use for a new project: Perl5 or Perl6. 6 wins so far except that it is missing Moo's lazy attributes. The two implementations I found in modules are missing the key functionality. Hence, my attempt write my own implementation.
Role vs. Class
First problem I've got into is the content of attribute's .package for one declared in a role. Consider the followin:
role HOW1 {
method compose ( Mu $class ) {
note "HOW1.compose";
nextsame;
}
}
role HOW2 {
method compose ( Mu $class ) {
note "HOW2.compose";
nextsame;
}
}
multi trait_mod:<is> (Attribute:D $attr, :$mooish!) {
note "Attribute's package.HOW: ", $attr.package.HOW;
note '$*PACKAGE.HOW: ', $*PACKAGE.HOW;
$attr.package.HOW does HOW1;
$*PACKAGE.HOW does HOW2;
}
class Foo {
has $.bar is mooish;
}
role FooRole {
has $.baz is mooish;
}
The output of the script follows:
Attribute's package.HOW: Perl6::Metamodel::ClassHOW.new
$*PACKAGE.HOW: Perl6::Metamodel::ClassHOW.new
HOW2.compose
HOW1.compose
Attribute's package.HOW: Perl6::Metamodel::GenericHOW.new
$*PACKAGE.HOW: Perl6::Metamodel::ParametricRoleHOW.new
HOW2.compose
As it is clearly seen from the output, applying a role to a metaclass always works for classes and only works for $*PACKAGE.HOW with roles. Use of $*PACKAGE instead of .package could be considered a solution, but not the one I'd really like to use. (Though, if there is no better way...)
Accessor
I would like to provide lazy functionality for private attributes too. Yes, this will be availabe with self!bar syntax only, but this is a sacrifice I'm willing to make. 😉 The problem is that all the examples of custome-made accessor I found so far are using Attribute.set_value() method which is way too low-level. I'd like to have something like this:
role MooishHOW {
method compose ( Mu $class ) {
my $accessor = $class.^add_private_method( 'bar1',
method () is rw {
note self.WHO, ".bar1";
Proxy.new(
FETCH => -> $o {
$!bar1;
},
STORE => method ( $val ) {
note "Storing";
$!bar1 = $val;
}
);
}
);
callsame;
}
}
multi trait_mod:<is> (Attribute:D $attr, :$mooish!) {
$attr.package.HOW does MooishHOW unless $attr.package.HOW ~~ MooishHOW;
}
class Foo {
has $.bar is mooish;
has $!bar1 is mooish;
method to-bar1 {
note "bar1 val:",self!bar1;
}
}
my $inst = Foo.new;
$inst.to-bar1;
But $!bar1 notation doesn't compile because of the scope (MooishRole). Are there a trick I'm missing which would allow referencing a private attribute on self?
Tricky one
Perhaps it is possible to make an attribute to be a Proxy container? This would greatly simplify the overall logic of laziness implementation.
I have answered all my questions by finally achieving the target and released AttrX::Mooish module.
So far, the answer for the first question is: no. $*PACKAGE is currently the only way.
Second question: have no answer, but the final code has to rely on set_value() anyway.
The tricky one happened to be possible: set_value() does binding of an attribue to a container making it possible to bind to a Proxy object. No need to for sacrifices, private attributes can be accessed directly with lazyness working on them.
Thanks everybody, your answers let me work around some rough edges!
I can call the gist method on the say built-in function:
&say.gist
sub say (| is raw) { #`(Sub|54790064) ... }
Why can't I call gist on while?
&while.gist
===SORRY!=== Error while compiling <unknown file>
Undeclared routine:
while used at line 1
Obviously while isn't a "routine" but say is. But I thought that all of the built-ins in Perl 6 were really functions that we could redefine.
I thought that all of the built-ins in Perl 6 were really functions that we could redefine.
while is not a routine or macro, but part of the syntax, defined in Perl6's grammar.
If you wanted to redefine it, you would have to create your own slang, which currently involves black magic.
For some reason I have yet to figure out, it only works when done in a module (otherwise, %*LANG seems not to be defined).
Let's call the module froop.pm6:
use nqp;
sub EXPORT {
nqp::bindkey(%*LANG, 'MAIN', nqp::atkey(%*LANG, 'MAIN').^mixin(role {
rule statement_control:sym<while> {
[$<sym>=froop{
$/.hash<sym>.^mixin: role {
method Str { 'while' }
}
}|$<sym>=until]<.kok> {}
<xblock>
}
}));
once Map.new
}
This replaces the while keyword (in statement position) with froop, eg
use froop;
my $i = 0;
froop $i < 5 { say $i++ }