Archive
@data».sparkle
While reading solutions to the PWC, I spotted a pattern. There where plenty of .map
-calls that contained only simple maths. It must not be so! Task 179-2 leans itself to use vector operations.
use v6.d;
constant @sparks = "▁" .. "█";
constant \steps = +@sparks - 1;
multi MAIN(
#| space separated list of numbers
*@data
) {
my $scale := @data.&{ (.max - .min) / steps };
put @sparks[((@data »-» @data.min) »/» $scale)».round].join;
}
multi MAIN(
#| output an example sparkline-graph
Bool :$test
) {
.&MAIN for <2 4 6 8 10 12 10 8 6 4 2>, <0 1 19 20>, <0 999 4000 4999 7000 7999>;
}
First, I define a character range (no need for .ord
here) and store the number of bar-graph steps. Then I calculate the scale like it is used in a map (those paper-things, that always work and don’t break when dropped). Now I can use that scale to shrink (or grow) values. I believe rounding is better here then to cut of to integers.
When we feed a list to a postcircumfix:<[ ]>
we get a list of values in return. However, we break the method-call chain when we do so. Since operators are just Sub
s with a funny syntax, we can solve that issue easily.
constant &sparks = {
constant @sparks = "▁" .. "█";
constant \steps = +@sparks - 1;
&postcircumfix:<[ ]>.assuming(@sparks) but role :: { has $.steps = steps }
}.();
my $scale := @data.&{ (.max - .min) / &sparks.steps };
((@data »-» @data.min) »/» $scale)».round.&sparks.join.put;
The constants are hidden within a block-scope and only steps
is exposed as part of a Sub
s “namespace”. By declaring &sparks
as constant, I move the currying to compile time.
Hypers are not getting much use in PWC-solutions. I believe another entry in raku-did-you-know is in order.
Suspicious introspection
One of my routines is acting suspicious. I’m almost certain it wilfully misinterprets arguments just so it can throw an exception. There is a way to spy on it.
sub suspicious($a, :$b, *@rest, *%opts) {
die('imaoutahere!');
CATCH {
put (my $sig = &?ROUTINE.signature.gist) ~ ' -> ' ~ $sig.trans(<*@ *%> => <@ %>).EVAL».raku.join(',');
}
}
suspicious(1, :2b, 3,4,5);
# OUTPUT: ($a, :$b, *@rest, *%opts) -> 1,:b(2),3 4 5,
imaoutahere!
in sub suspicious at tmp/2021-03-08.raku line 1910
in block <unit> at tmp/2021-03-08.raku line 1917
Please note, that the CATCH
-block doesn’t do anything to the exception. It is just there to trigger the debugging code. The proper place to put that would be a macro. Alas, macro
can’t inject CATCH
-blocks that are actually used as exception handlers. It will have to wait for RakuAST, what would also allow me to replace the EVAL-shenanigans with proper code. Another entry in macro-ideas.txt
until then.
Defeating definedness
In his latest blogpost, p6steve went through a lot of trouble to enforce definedness. I managed to shorten it a bit.
None: Nil;
subset Option of Any where Any:D | None;
my Option $defined = 42;
my Option $nothing = None;
my Option $undefined = Mu;
.&dd for $defined, $nothing, $undefined;
# OUTPUT: Type check failed in assignment to $undefined; expected Option but got Mu (Mu)
I would really like to see the content of $nothing
, and while I’m on it show why Steve’s hard labour was in vain.
try {
my Option $defined = 42;
my Option $nothing = None;
my Option $undefined = Mu;
.&dd for $defined, $nothing, $undefined;
CATCH { default { note .message; .resume } }
}
# OUTPUT: Type check failed in assignment to $undefined; expected Option but got Mu (Mu)
# Int $defined = 42
# Label $nothing = Label.new(name => "None", file => "tmp/2021-03-08.raku", line => 1880)
# Any $undefined = Any
Raku is a dynamic, late bound language. There are language constructs, like resumable exceptions and NativeCall, that can defeat any type-check. Trying to use Raku like Rust, Haskell or any other strictly typed language will eventually fail. No amount of :D
s will substitute testing, testing and even more testing.
I hope not to have tested p6steve’s patience and try to calm the waves with a new repo.
Symbolism
On IRC deoac wished to know how to print the name of a variable. This question is ambiguous. To get the name of the container is easy.
my $foo;
say $foo.VAR.name;
# OUTPUT: $foo
If binding or constants involved, things get odd or fail.
my $bar := $foo;
constant $never-changes = 'war';
# OUTPUT: $foo
No such method 'name' for invocant of type 'Str'. Did you mean any of
these: 'Date', 'Num', 'are', 'none', 'note', 'take'?
The second meaning of “variable” might be “symbol” for deoac’s use case. Getting hold of a symbol name requires early compile time measures.
use experimental :macros;
macro symbol-name($symbol) {
quasi { $symbol.Str }
}
say symbol-name($foo);
say symbol-name($bar);
say symbol-name($never-changes);
# OUTPUT: ($foo)
($bar)
($never-changes)
The value of $symbol
is an AST object and stringifies (in this case) to a list of Str
. I didn’t dig deeper, because with RakuAST things will change.
However, this once again shows that we might do better by explaining what my
actually does instead of assuming that one can guess what we mean when using the term “variable”.