Subsetting Blocks (or Callables in general) - signature

This question was originally posted by lookatme in the Perl6 IRC channel. The original intention is to constrain a Callable by using subsets or any other possible way. It works in signatures, however this:
subset WalkCb of Callable where *.signature ~~ :(Int $a);
my WalkCb $x = sub (Int $a) { return $a; };
returns
«Type check failed in assignment to $x; expected WalkCb but got Sub (sub (Int $a) { #`(Sub...)␤ in block <unit> at <tmp> line 1␤␤»
Other variants of the declaration return similar messages. What would be the correct syntax?

A where constraint can take a block (lambda) or a statement.
… where { $_ == 42 }; # block
… where * == 42; # lambda
… where $_ == 42; # statement
The thing is that ~~ doesn't participate in the creation of WhateverCode lambdas (*)
So what you are really doing is creating a lambda of *.signature inside of a larger where statement (not lambda).
constant $lambda = *.signature;
subset WalkCb of Callable where $lambda ~~ :(Int $);
Which will of course never return True as the lambda has a signature of :(;; $ is raw)
So instead just write the statement form. (using implicit $_)
subset WalkCb of Callable where .signature ~~ :(Int $);
my WalkCb $x = sub (Int $a) { return $a }
say $x(42); # 42␤

It appears the WhateverCode parsing messes up in this case. So if you specify a block in the where:
subset WalkCb of Callable where { .signature ~~ :(Int $) }
my WalkCb $x = sub (Int $a) { return $a; }
it appears to work as expected. Also note that I removed the ; at the end of the lines, as a } at the end of a line is also the end of a statement. And that I removed the "a" from the signature: the name of of positional parameters in signature smartmatching is ignored.

Related

Why does using a variable as a function parameter cause a type mismatch error in a later function call?

def make-list [val?: string] {
["f1" "f2"]
}
def use-list [list: list] {
$list | each { |it| $it + "!" }
}
let $val = "abc"
let $l = (make-list $val)
use-list $l
In this code, I have two functions, one to make a list, and another to consume that list. For the purposes of this MRE, make-list returns a simple hard-coded list, and use-list prints each element with an exclamation mark added to the end.
If this script executes correctly, it should print a list with two elements "f1!" and "f2!".
make-list has an optional parameter. In the code above, I create a $val variable, then pass that into make-list, and store the result of the pipeline to $l. Executing $l | describe instead of use-list $l prints list<string>, so I'm confident that $l is indeed a list.
The code above throws the following compile error:
Error: nu::parser::type_mismatch (link)
× Type mismatch.
╭─[/.../test.nu:10:1]
10 │ let $l = (make-list $val)
11 │ use-list $l
· ─┬
· ╰── expected List(Any), found String
╰────
However, I can modify the let $l ... line to allow the script to compile and execute correctly. Both of these options will allow the script to compile and returns the expected result.
The parameter for make-list can be removed (because the parameter is optional).
let $l = (make-list)
The parameter for make-list can be replaced by a string literal
let $l = (make-list "abc")
I don't understand why using a variable as a parameter call is suddenly causing a type issue with the use-list function call. What am I doing wrong here?
As an interesting side note, and this might explain the "...found String" part of the error message, if I change the make-list parameter to an int:
def make-list [val?: int] {
["f1" "f2"]
}
# ...
let $val = 3
let $l = (make-list $val)
use-list $l
It will fail to compile with the same kind of error, but the "found" type will reflect the updated parameter type.
Error: nu::parser::type_mismatch (link)
× Type mismatch.
╭─[/.../test.nu:10:1]
10 │ let $l = (make-list $val)
11 │ use-list $l
· ─┬
· ╰── expected List(Any), found Int
╰────
Tested using Nushell version 0.74.0 and 0.75.0 on Arch Linux and Windows 11, respectively.
Something seems to "go wrong" with the variable binding. Your scenario does work if the literal string is being evaluated in a subexpression before it is being bound:
let $val = ("abc")
# or
let $val = do { "abc" }
Even a seemingly useless type casting later on does the trick:
let $val = "abc"
let $l = (make-list ($val | into string))
This is also consistent with other types. Adapting your int example:
def make-list [val?: int] { … }
…
let $val = 123 # fails
let $val = (123) # works
Likewise, having a do closure, or type-casting using into int, do work as well.
Not of importance, however, is the syntax of the variable binding. With the $ sign omitted, all examples from above still work or fail under the same circumstances, e.g.:
let val = "abc" # fails
let val = ("abc") # works
As expected, using let l = instead of let $l = also yields the same results.

Can I write an object that behaves like a hash?

In Perl there is tie. Python supports various protocols so that objects can behave like i.e. a dictionary. Is there something similar in Raku?
I.e. Can I define an object that behaves like a Hash? That is: can I write $myobject<key> to end up in a routine that I can specify myself?
Perl has the Hash feature baked into the language.
So to extend it so that an object behaves like a Hash you needed to tell the runtime to do something different.
That is not the case for Raku.
A Hash in Raku is just another object.
The Hash indexing operation is just another operator that can be overloaded the same way you can overload other operators.
So you can create your own object that has the same features as a Hash, or even just inherit from it.
class Foo is Hash {
}
class Bar does Associative {
# delegate method calls to a Hash object
has %!hash handles Hash;
}
The reason to have does Associative is so that you can use it as the type to back an associative variable. (Hash already does Associative so you would inherit that too.)
my %f is Foo;
my %b is Bar;
To find out which methods you can write to implement Hash indexing operations you could look at the methods that Hash implements.
Since we know that methods that automatically get called are uppercase, we only need to look at them.
Hash.^methods.map(*.name).grep(/^<:Lu + [-]>+$/)
# (STORE BIND-KEY WHICH AT-KEY ASSIGN-KEY DELETE-KEY
# DUMP BUILDALL ASSIGN-KEY EXISTS-KEY AT-KEY STORE ACCEPTS BUILDALL)
It should be fairly obvious that the methods ending with -KEY are the ones we would want to write. (The other ones are mostly just object artifacts.)
You currently don't have to write any of them to make your object type Associative.
If you don't write a particular method, that feature won't work.
class Point does Associative {
has Real ($.x, $.y);
multi method AT-KEY ( 'x' ){ $!x }
multi method AT-KEY ( 'y' ){ $!y }
multi method ASSIGN-KEY ( 'x', Real $new-value ){ $!x = $new-value }
multi method ASSIGN-KEY ( 'y', Real $new-value ){ $!y = $new-value }
multi method EXISTS-KEY ( 'x' --> True ){}
multi method EXISTS-KEY ( 'y' --> True ){}
multi method EXISTS-KEY ( Any --> False ){}
}
my %p is Point;
%p<x> = 1;
%p<y> = 2;
say %p.x; # 1
say %p.y; # 2
Note that above has a few limitations.
You can't assign to more than one attribute at a time.
%p< x y > = 1,2;
You can't assign the values in the declaration.
my %p is Point = 1,2;
my %p is Point = x => 1, y => 2;
In the multi-assignment, the method that gets called is AT-KEY. So to make it work those must be marked as raw or rw
class Point does Associative {
…
multi method AT-KEY ( 'x' ) is rw { $!x }
multi method AT-KEY ( 'y' ) is rw { $!y }
…
}
…
%p<x y> = 1,2;
That takes care of multi assignment, but that still leaves the initialization in the declaration.
If you declared an attribute as is required the only way to write it would be:
my %p := Point.new( x => 1, y => 2 );
If you didn't do that you could implement STORE.
class Point does Associative {
…
method STORE ( \list ) {
($!x,$!y) = list.Hash<x y>
}
}
my %p is Point = x => 1, y => 2;
That also makes it so that you can also assign to it later.
%p = x => 3, y => 4;
Which is possibly not what you wanted.
We can fix that though.
Just make it so that there has to be an :INITIALIZE argument.
class Point does Associative {
…
method STORE ( \list, :INITIALIZE($) is required ) {
($!x,$!y) = list.Hash<x y>
}
}
my %p is Point = x => 1, y => 2;
# %p = x => 3, y => 4; # ERROR
In the case of Point we might want to be able to declare it wit a list of two elements:
my %p is Point = 1,2;
Or by name:
my %p is Point = x => 1, y => 2;
To do that we can change how STORE works.
We'll just look at the first value in the list and check if it is Associative.
If it is we will assume all of the arguments are also Associative.
Otherwise we will assume that it is a list of two values, x and y.
class Point does Associative {
…
method STORE ( \list, :INITIALIZE($) is required ) {
if list.head ~~ Associative {
($!x,$!y) = list.Hash<x y>
} else {
($!x,$!y) = list
}
}
}
my %a is Point = x => 1, y => 2;
my %b is Point = 1,2;
In raku the syntactical <> seems to be an postcircumfix operator that can be overloaded via a multi method AT-KEY and EXISTS-KEY as described in https://docs.raku.org/language/subscripts#Methods_to_implement_for_associative_subscripting
Can I define a object that behaves like an hash? That is: if I write $myobject<key> I endup in a function that I can specify myself?
The short answer is. No, there is not in core Raku. But there is a module that makes it easy for you to do, having only to define 5 methods to create full functionality as a "real" Hash: Hash::Agnostic
The longer answer is: read the other answers to this question :-)

stuck at understanding "$worksheet->add_write_handler(qr[\w], \&store_string_widths);" line in the below piece of code

My final goal for my first perl program :To create an excel sheet for reporting purpose and email the sheet as an attachment.
I have reached till creating a csv file. now i wanted to convert this to excel sheet and autofit the content.
I have an example code in our environment,could someone take time to explain each line on the below code, it would be very grateful.
outputfile,urloutputfile,scomoutputfile - are the csv files, now being converted to excel sheets.
Please explain how an element is being passed to the other function also.
my $parser = Text::CSV::Simple->new;
my $workbook = Excel::Writer::XLSX->new($auditxl);
my #totcsvlist;
push(#totcsvlist,$outputfile);
push(#totcsvlist,$urloutputfile);
push(#totcsvlist,$scomoutputfile);
my #data;
my $subject = 'worksheet';
foreach my $totcsvlis (#totcsvlist)
{
undef #data;
chomp($totcsvlis);
if ($totcsvlis eq $outputfile) { $subject="Service Status"; }
if ($totcsvlis eq $urloutputfile) { $subject="URL Status"; }
if ($totcsvlis eq $scomoutputfile) { $subject="SCOM Agent Status"; }
#data = $parser->read_file($totcsvlis);
my $headers = shift #data;
import_data($workbook, $subject, $headers, \#data);
}
$workbook->close();
sub autofit_columns {
my $worksheet = shift;
my $col = 0;
for my $width (#{$worksheet->{__col_widths}}) {
$worksheet->set_column($col, $col, $width) if $width;
$col++;
}
}
sub import_data {
my $workbook = shift;
my $base_name = shift;
my $colums = shift;
my $data = shift;
my $limit = shift || 50_000;
my $start_row = shift || 1;
my $bold = $workbook->add_format();
$bold->set_bold(1);
$bold->set_bg_color('gray');
$bold->set_border();
my $celbor = $workbook->add_format();
$celbor->set_border();
my $worksheet = $workbook->add_worksheet($base_name);
$worksheet->add_write_handler(qr[\w], \&store_string_widths);
my $w = 1;
$worksheet->write('A' . $start_row, $colums, $bold);
my $i = $start_row;
my $qty = 0;
for my $row (#$data) {
$qty++;
$worksheet->write($i++, 0, $row,$celbor);
}
autofit_columns($worksheet);
warn "Convereted $qty rows.";
return $worksheet;
}
sub autofit_columns {
my $worksheet = shift;
my $col = 0;
for my $width (#{$worksheet->{__col_widths}}) {
$worksheet->set_column($col, $col, $width + 5) if $width;
$col++;
}
}
sub store_string_widths {
my $worksheet = shift;
my $col = $_[1];
my $token = $_[2];
return if not defined $token; # Ignore undefs.
return if $token eq ''; # Ignore blank cells.
return if ref $token eq 'ARRAY'; # Ignore array refs.
return if $token =~ /^=/; # Ignore formula
return if $token =~ m{^[fh]tt?ps?://};
return if $token =~ m{^mailto:};
return if $token =~ m{^(?:in|ex)ternal:};
my $old_width = $worksheet->{__col_widths}->[$col];
my $string_width = string_width($token);
if (not defined $old_width or $string_width > $old_width) {
$worksheet->{__col_widths}->[$col] = $string_width;
}
return undef;
}
sub string_width {
return length $_[0];
}
I have tried to search and read modules used in the above code, but over head.
https://github.com/jmcnamara/spreadsheet-writeexcel/blob/master/examples/autofit.pl
-- has similar code and has provided a basic over view. but i would like to understand in detail.
Thank you so much in advance.
Regards,
Kaushik KM
Here is the documentation for the add_write_handler() method call. It says:
add_write_handler( $re, $code_ref )
This method is used to extend the Excel::Writer::XLSX write() method
to handle user defined data.
And later, it says:
The add_write_handler() method take two arguments, $re, a regular
expression to match incoming data and $code_ref a callback function
to handle the matched data
So, here you have a method call that takes two arguments. The first is a regex that tells the object what type of data this new write handler is used for. The second is a reference to the subroutine that should be used as the write handler for data that matches the regex.
The regex you have is qr[\w]. The actual regex bit of that is \w. And that just means "match a word character". The qr is to compile a string into a regex and the [ ... ] is just the delimiter for the regex string (qr/.../ is one of a class of Perl operators that allows you to use almost any character you want as a delimiter).
So, if your object is called on to write some data that contains at least one word character, the subroutine which is given as the second argument is used. But we take a reference to the subroutine.
Elsewhere in your code, you define the store_string_widths() subroutine. Subroutines in Perl are a lot like variables, and that means that they have their own sigil. The sigil for a subroutine is & (like the $ for scalar and # for arrays). You very rarely need the & in modern Perl code, so you won't see it used very often. One place that it is still used, is when we take a reference to a subroutine. You take a reference to any variable by putting a slash in front of the variable's full name (like \#array or \%hash) and subroutines are no different. So \&store_string_widths means "get a reference to the subroutine called store_string_widths()".
You say that this is your first Perl program. I have to say that this feels a little ambitious for your first Perl code. I don't cover references at all in my two-day beginners course and on my intermediate course I cover most references, but on mention subroutine references in passing. If you can understand references enough to get this all working, then I think you're doing really well.

Groovy's "optional return" semantics

In Groovy, the return statement is optional, allowing you to write methods like:
def add(a, b) {
a + b
}
...which adds a and b and returns the result to the caller.
However, I'm wondering what the semantics are when the method has multiple return "statements". For example, in Java you might have:
String append(String a, String b) {
if (a == null) {
return b;
}
return a + b;
}
This could (hypothetically) be translated to Groovy like:
def append(a, b) {
if (! a) {
b
}
a + b
}
However, in this case, how does Groovy know that b inside of the if statement should be returned? Or does it not? I assume that Groovy cannot simply treat any statement whose result is unused as a return, correct? Are there any clearly defined semantics for how the "optional return" feature behaves in this case?
The page you linked (rather tersely) describes the exact semantics:
Notice that the return statement is optional at the end of methods.
So the b in that if block would never be returned unless you explicitly returned it. In practice this means that the return value will be the result of the last statement evaluated, so if your example were
def append(a, b) {
if (!a) { b }
else { a + b }
}
Then the result would be b if !a is true and a + b otherwise.
The result of a call to a void function is null, so if the example were
def append(a,b) {
if (!a) { b }
else { a + b }
println "debug: $a $b"
}
Then append would always return null.
My own rule of thumb for this is to always use an explicit return statement if the method or closure contains more than one statement. I think relying on the implicit return statement in more complex methods is dangerous since if anyone adds a line to the end of the method they will change the return value even though they most likely didn't intend to.

extract arguments from function with scripting

I have a file with function prototypes like this:
int func1(type1 arg, int x);
type2 funct2(int z, char* buffer);
I want to create a script (bash, sed, awk, whatever) that will print
function = func1 // first argument type = type1// second argument type = int
function = func1 // first argument type = int// second argument type = char*
In other words, tokenize every line and print the function names and arguments. Additionally I would like to hold these tokens as variables to print them later, eg echo $4.
An alternative approach would be to compile with "-g" and read the debug information.
This answer may help you read the debug information and figure out function parameters (it's Python, not bash, but I'd recommend using Python or Perl instead of bash anyway).
The resulting solution would be much more robust than anything based on text parsing. It will deal with all the different ways in which a function might be defined, and even with crazy things like functions defined in macros.
To convince you better (or help you get it right if you're not convinced), here's a list of test cases that may break your parsing:
// Many lines
const
char
*
f
(
int
x
)
{
}
// Count parenthesis!
void f(void (*f)(void *f)) {}
// Old style
void f(a, b)
int a;
char *b
{
}
// Not a function
int f=sizeof(int);
// Nesting
int f() {
int g() { return 1; }
return g();
}
// Just one
void f(int x /*, int y */) { }
// what if?
void (int x
#ifdef ALSO_Y
, int y
#endif
) { }
// A function called __attribute__?
static int __attribute__((always_inline)) f(int x) {}
here's a start.
#!/bin/bash
#bash 3.2+
while read -r line
do
line="${line#* }"
[[ $line =~ "^(.*)\((.*)\)" ]]
echo "function: ${BASH_REMATCH[1]}"
echo "args: ${BASH_REMATCH[2]}"
ARGS=${BASH_REMATCH[2]}
FUNCTION=${BASH_REMATCH[1]}
# break down the arguments further.
set -- $ARGS
echo "first arg type:$1 , second arg type: $2"
done <"file"
output
$ ./shell.sh
function: func1
args: type1 arg, int x
first arg type:type1 , second arg type: arg,
function: funct2
args: int z, char* buffer
first arg type:int , second arg type: z,

Resources