Why is Perl 6's unwrap method a method of Routine? - raku

There's an unwrap method, but the way it seems I'm supposed to use
it isn't the way it should be used. It seems like it should either be a
standalone routine or a method in a different class. What am I missing?
It appears that it doesn't care what its invocant is as long as it
gets the right Routine::WrapHandle thingy as an argument. In this
example, I wrap a subroutine and get back a WrapHandle:
sub add-two-numbers ( $n, $m ) { $n + $m }
sub do-some-stuff ( $n, $m, $o ) {
add-two-numbers( $n max $m, $m max $o );
}
put do-some-stuff( 5, 10, 15 );
# now I want to look into do-some-stuff to see what it's
# passing
my $wraphandle = &add-two-numbers.wrap: {
say "Arguments were (#_[])";
callwith( |#_ );
}
put do-some-stuff( 5, 10, 15 );
Then, I can create a different and unrelated routine and call unwrap
on that:
my &routine = sub { 1 };
&routine.unwrap( $wraphandle );
put do-some-stuff( 5, 10, 15 );
The invocant to unwrap seems superfluous. Indeed, I can call it as a
class method and it still works:
Routine.unwrap( $wraphandle );
But, it seems this should either be a routine (since the invocant
doesn't matter):
unwrap( $wraphandle ); # doesn't exist
Or a method on Routine::WrapHandle since that's the source of the
behavior:
$wraphandle.unwrap; # but, this method doesn't exist in that class
So, what am I missing about this method?

At a guess, the interface was designed one way (with the routine keeping the information and being able to remove 'passive' handles), but implemented another (with the handle already keeping all required information so it can unwrap itself).
As to the notion that unwrap should perhaps be a method on the handle: It actually is, but the method is called restore, which Routine.unwrap merely delegates to (cf core/Routine.pm:110..113):
method unwrap($handle) {
$handle.can('restore') && $handle.restore() ||
X::Routine::Unwrap.new.throw
}
If you want the full story, besides core/Routine.pm, there's also Perl6::Metamodel::WrapDispatcher defined in Perl6/Metamodel/Dispatchers.nqp. From what I can see, it certainly should be possible to implement the original design I conjectured, but it would need someone feeling strongly enough about the issue to actually do it...

Related

Enforcing read-only attributes from the metaclass

Yes, still going with this. My impression is that there's this powerful facility in Raku, which is not really easy to use, and there's so little documentation for that. I'd like to kind of mitigate that.
In this case, I'm trying to force attributes to be read-only by default, to make immutable classes. Here's my attempt:
my class MetamodelX::Frozen is Metamodel::ClassHOW {
method compose_attributes($the-obj, :$compiler_services) {
my $attribute-container = callsame;
my $new-container = Perl6::Metamodel::AttributeContainer.new(
:attributes($attribute-container.attributes),
:attribute_lookup($attribute-container.attribute_table),
:0attr_rw_by_default
);
$new-container.compose_attributes($the-obj, $compiler_services);
}
}
my package EXPORTHOW {
package DECLARE {
constant frozen = MetamodelX::Frozen;
}
}
I'm calling that from a main function that looks like this:
use Frozen;
frozen Foo {
has $.bar;
method gist() {
return "→ $!bar";
}
}
my $foo = Foo.new(:3bar);
say $foo.bar;
$foo.bar(33);
I'm trying to follow the source, that does not really give a lot of facilities to change attribute stuff, so there seems to be no other way that creating a new instance of the container. And that might fail in impredictable ways, and that's what it does:
Type check failed in binding to parameter '$the-obj'; expected Any but got Foo (Foo)
at /home/jmerelo/Code/raku/my-raku-examples/frozen.raku:7
Not clear if this is the first the-obj or the second one, but any way, some help is appreciated.

How to repeat Mono while not empty

I have a method which returns like this!
Mono<Integer> getNumberFromSomewhere();
I need to keep calling this until it has no more items to emit. That is I need to make this as Flux<Integer>.
One option is to add repeat. the point is - I want to stop when the above method emits the first empty signal.
Is there any way to do this? I am looking for a clean way.
A built-in operator that does that (although it is intended for "deeper" nesting) is expand.
expand naturally stops expansion when the returned Publisher completes empty.
You could apply it to your use-case like this:
//this changes each time one subscribes to it
Mono<Integer> monoWithUnderlyingState;
Flux<Integer> repeated = monoWithUnderlyingState
.expand(i -> monoWithUnderlyingState);
I'm not aware of a built-in operator which would do the job straightaway. However, it can be done using a wrapper class and a mix of operators:
Flux<Integer> repeatUntilEmpty() {
return getNumberFromSomewhere()
.map(ResultWrapper::new)
.defaultIfEmpty(ResultWrapper.EMPTY)
.repeat()
.takeWhile(ResultWrapper::isNotEmpty)
}
// helper class, not necessarily needs to be Java record
record ResultWrapper(Integer value) {
public static final ResultWrapper EMPTY = new ResultWrapper(null);
public boolean isNotEmpty() {
return value != null;
}
}

Apply a proxy to a variable (not an attribute) using traits

This question is a near-duplicate of Apply a proxy using traits. However, that question dealt with applying a proxy to an Attribute, and I would like to do the same thing for a Variable. From Jonathan's answer, I understand that I
need to arrange for the Proxy to be bound into the attribute, so that there's a Proxy there rather than a Scalar container that is usually created by class initialization logic.
However, I can't seem to bind successfully to a Variable:D, even at compile time. (Including with nqp::bind). I'd greatly appreciate any pointers in the correct direction.
(Ideally, I'd like to support using the variable/trait with assignment syntax. In a perfect world, I'd have syntax like:
my $thing is custom-proxy = 42;
And the result of that would be that $thing is containerized inside the Proxy, but not in a Scalar. But if that's not possible, I'd settle for getting it working with binding via :=.
[EDIT: building on the accepted answer below, it is possible to mostly do this with the following code:
multi trait_mod:<is>(Variable \v, :$tom) {
v.block.add_phaser(
'ENTER',
v.willdo(<-> $_ {
$_ = Proxy.new:
STORE => -> $, $v { say "store $v" },
FETCH => { say "fetch!"; 42}
}, 1))
}
This works for variables that are not initialized to a different value or for state variables on calls to the function other than the first.
You can always bind.
my $actual-thing = 42;
my $thing := Proxy.new(
FETCH => anon method fetch () {
say 'fetch';
$actual-thing
},
STORE => anon method store ($new) {
say 'store ',$new;
$actual-thing = $new
}
);
say $thing;
$thing = 5;
say $thing;
Which currently results in the following.
fetch
fetch
fetch
fetch
fetch
fetch
fetch
42
store 5
fetch
fetch
fetch
fetch
fetch
fetch
fetch
5
(The repeated FETCH calls are a known limitation.)
If you wanted to have syntax like
my $thing is custom-proxy = 42;
You would need to start with
multi trait_mod:<is> ( Variable:D \var, :$custom-proxy! ){
…
}
The problem is that currently doing it this way requires a lot of deep Rakudo/nqp knowledge that I do not possess.
For example the code behind my $var is default('value') looks a bit like this:
multi sub trait_mod:<is>(Variable:D $v, Mu :$default!) {
my $var := $v.var;
my $what := $var.VAR.WHAT;
my $descriptor;
{
$descriptor := nqp::getattr($var, $what.^mixin_base, '$!descriptor');
CATCH {
my $native = $v.native($what);
…
}
}
…
$descriptor.set_default(nqp::decont($default));
# make sure we start with the default if a scalar
$var = $default if nqp::istype($what, Scalar);
}
Why does that have $what.^mixin_base?
I have no idea.
Why isn't $!descriptor accessible something like $v.var.descriptor?
I have no idea.
How do we change $v.var.VAR from a Scalar to a Proxy?
I have no idea.
Is that last one doable? (From within a trait_mod:<is>)
I am fairly certain that the answer is yes.
My 2d[1]:
I'd settle for getting it working with binding via :=.
sub custom-proxy is rw { Proxy.new: FETCH => { 42 }, STORE => { ... } }
my $variable := custom-proxy;
say $variable; # 42
In a perfect world, I'd have syntax like:
my $thing is custom-proxy = 42;
Aiui, that's #Larry's intent.
But, as you presumably know, if a type (eg role custom-proxy { ... }) is applied using an is trait to a scalar variable (eg my $variable is custom-proxy) then the compiler emits a compile time error message (is trait on $-sigil variable not yet implemented).
I can't seem to bind successfully to a Variable:D, even at compile time
First, let's clarify what a Variable is, and what you would need to successfully bind to:
multi trait_mod:<is>(Variable \var, :$foo!) { say var.var.VAR.WHAT } # (Scalar)
my $variable is foo;
You might think you could bind to var. But the compiler is passing an lvalue, so you're not going to be able to alter it.
You might think you could bind to var.var, which is an attribute of a Variable. (I explain what a Variable is, and its var attribute, and why I had to write "varvarVAR!" in the above code, here.)
The SO you linked shows how to alter the value bound to an attribute in some object:
$a.set_build: -> \SELF, | {
$a.set_value: SELF, Proxy.new:
STORE => -> $, $val { say "store $val" },
FETCH => { say "fetch!"; 42 }
}
So perhaps you could use that approach to alter the .var attribute of a Variable?
Unfortunately, "setting build logic" is used to "bind the attribute ... at each object creation", (hence "you'll be overriding any initial default value").
So I don't think this technique is going to help in this case because the Variable, and hence its .var attribute, has presumably already been built by the time the Variable is passed to the is trait.
In summary, while a trait is called at compile-time, I think it's called too late because the var attribute has already been permanently bound.
My guess is that altering Raku(do) so that the Variable's .var attribute becomes writable, or using metaprogramming to dive underneath Variable's public API to force through a change, would be beyond fraught, unreasonably complicating the compiler's variable handling code and/or swapping out codegen optimization logic for pessimization logic.
This may be behind #Larry's speculation that a more controlled is type on scalar variables will one day be implemented.
Footnotes
[1] My two (pennies | dogecoin).

Using $/ is not exactly the same as using any other variable in grammar actions

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)

Passing custom parameters in render function

I have below code to create column:
DTColumnBuilder.newColumn(null).withTitle('Validation').renderWith(validationRenderer)
and render function:
function validationRenderer(data, type, full, meta) {
.......
}
Now, I want to pass custom parameters to validationRenderer so that I can access it inside the function, like below:
DTColumnBuilder.newColumn(null).withTitle('Validation').renderWith(validationRenderer('abc'))
function validationRenderer(data, type, full, meta, additionalParam) {
// do something with additionalParam
}
I could not find it in the documentation but there must be something to pass additional parameters in meta as per the reference from here
Yes, you can. Or, better, you technically can, but you may use a clever workaround to handle your issue.
I had this issue today, and found a pretty sad (but working) solution.
Basically, the big problem is that the render function is a parameter passed to the datatable handler, which is (of course) isolated.
In my case, to make a pratical example, I had to add several dynamic buttons, each with a different action, to a dynamic datatable.
Apparently, there was no solution, until I thought the following: the problem seems to be that the renderer function scope is somewhat isolated and unaccessible. However, since the "return" of the function is called only when the datatable effectively renders the field, you may wrap the render function in a custom self-invoking-anonymous-function, providing arguments there to use them once the cell is being rendered.
Here is what I did with my practical example, considering the following points:
The goal was to pass the ID field of each row to several different custom functions, so the problem was passing the ID of the button to call when the button is effectively clicked (since you can't get any external reference of it when it is rendered).
I'm using a custom class, which is the following:
hxDatatableDynamicButton = function(label, onClick, classNames) {
this.label = label;
this.onClick = onClick;
this.classNames = this.classNames || 'col5p text-center';
}
Basically, it just creates an instance that I'm later using.
In this case, consider having an array of 2 different instances of these, one having a "test" label, and the other one having a "test2" label.
I'm injecting these instances through a for loop, hence I need to pass the "i" to my datatable to know which of the buttons is being pressed.
Since the code is actually quite big (the codebase is huge), here is the relevant snippet that you need to accomplish the trick:
scope.datatableAdditionalActionButtons.reverse();
scope._abstractDynamicClick = function(id, localReferenceID) {
scope.datatableAdditionalActionButtons[localReferenceID].onClick.call(null, id);
};
for (var i = 0; i < scope.datatableAdditionalActionButtons.length; i++) {
var _localReference = scope.datatableAdditionalActionButtons[i];
var hax = (function(i){
var _tmp = function (data, type, full, meta) {
var _label = scope.datatableAdditionalActionButtons[i].label;
return '<button class="btn btn-default" ng-click="_abstractDynamicClick('+full.id+', '+i+')">'+_label+'</button>';
}
return _tmp;
})(i);
dtColumns.unshift(DTColumnBuilder.newColumn(null).notSortable().renderWith(hax).withClass(_localReference.classNames));
}
So, where is the trick? the trick is entirely in the hax function, and here is why it works: instead of passing the regular renderWith function prototype, we are using a "custom" render, which has the same arguments (hence same parameters) as the default one. However, it is isolated in a self invoking anonymous function, which allows us to arbitrarely inject a parameter inside it and, so, allows us to distinguish, when rendering, which "i" it effectively is, since the isolated scope of the function is never lost in this case.
Basically, the output is as follow:
And the inspection actually shows that elements are effectively rendered differently, hence each "i" is being rendered properly, while it wouldn't have if the function wouldn't have been wrapped in a self invoking anonymous function:
So, basically, in your case, you would do something like this:
var _myValidator = (function(myAbcParam){
var _validate = function (data, type, full, meta) {
console.log("additional param is: ", myAbcParam); // logs "abc"
return '<button id="'+myAbcParam+'">Hello!</button>'; // <-- renders id ="abc"
}
return _validate ;
})('abc');
DTColumnBuilder.newColumn(null).withTitle('Validation').renderWith(_myValidator);
// <-- note that _myValidator is passed instead of "_myValidator()", since it is already executed and already returns a function.
I know this is not exactly the answer someone may be expecting, but if you need to accomplish something that complex in datatable it really looks like the only possible way to do this is using a self invoking anonymous function.
Hope this helps someone who is still having issues with this.