Adjective Noun

Home

Perl 6 on Rails

2017-06-16 15:33, Tags: perl functional

I saw this interesting article titled Railway Oriented Programming by Scott Wlaschin. Initially I just clicked through the slides and the gist, as I understand it, is to define "chainable" functions that also encapsulate error handling. I later watched the video of the talk and highly recommend it. It's interesting and engaging, and Scott has a good sense of humour.

Ultimately it's about allowing you to write your programs focusing on the "happy path", ie. the code path when everything goes right. We often think about our code along the happy path. Unfortunately, you then have to add additional code for handling errors, typically by throwing if/else or try/catch blocks everywhere and making your code ugly in the process. An alternate error handling methodology is a concept he refers to as "two-track functions".

These functions can accepts 2 different types of input, and return 2 different types of output... like a railway station with two tracks, ya see! In the example, these types indicate either Success or Failure, and if several of your functions can accept and return both, you can chain them together easily. The talk actually covers some oft' confusing functional concepts like "monads" (scary quotes!) in an approachable way. Just go and watch the talk.

The language used is F#, which is billed as a functional language. Now, I'm no functional programmer. My vocabulary extends primarily to the so-called "scripting" languages: Perl, Python, Powershell, Bash... but Perl 6 is kind of a distant relative of another functional language, Haskell. One of the initial Perl 6 compilers (called Pugs) was implemented in Haskell.

Although Pugs is no longer actively developed, it was an important step in the path towards Rakudo, which is currently the most developed Perl 6 compiler. Pugs helped solidify a lot of the ideas in Perl 6, and as the language was being implemented by people familiar with Haskell, there was a cross-pollination of some functional ideas from Haskell to Perl 6... but enough with the history lesson. I decided to see how I could implement something similar in Perl 6, just for a bit of fun.

I don't want to build and entire app here that receives input from a browser, validates an email address, updates a database, etc. I'm just going to do the first thing - validate an email - and I'm going to define some very banal checks on my email validator to make the code simple. Email addresses must contain an @, must be in lowercase, and mustn't be a .io... because I said so! This is not an effective way to validate an email address.

I start by defining my validator functions, which look like this.

sub contains-at( Str $s ) {
    when not $s.contains('@') {
        fail("Address is missing '@' symbol")
    }
    default { $s }
}

sub is-lower( Str $s ) {
    when $s ne $s.lc {
        fail("Address is not lower-case")
    }
    default { $s }
}

sub not-io( Str $s ) {
    when not $s.ends-with('.io') {
        fail("I don't like '.io' domains")
    }
    default { $s }
}

For the conditional check inside my validators I'm using a when block, which comes from the given/when/default construct, Perl's friendlier sounding version of switch/case. I've used this in place of if/else because when automatically short-circuits. It doesn't really matter in this check for which there is only 2 branches... but if I later decide to throw in additional checks, I can just add more when blocks.

In isolation, these functions are fine. If I pass an email address to each one, it will perform a validation check and if it passes the check, the address is returned. That means if I input a "valid" email address, I could chain them together and it would pass through each validator. This is the happy path... but if one of the validations fails, it will pass a Failure object to the next function which is expecting a Str and die. These functions all have one-track input, but two-track output. Here's a slide from the talk.

So how to we convert it to two-track input? Scott - staying true to his train track analogy - builds a kind of adaptor block. He does this with a "bind" operation, which binds an additional function to his validators. As the slide above implies, this adapter block handles a possible Failure input, turning it into a true two-track function, which can compose (chain) nicely with other two-track functions... but can I achieve this in Perl 6? I think so?

I reiterate that I'm barely knowledgeable on functional programming concepts, so I could be wrong here, but it seems that this concept of "bind" is similar to "wrap" in Perl 6. wrap is method on a Routine (aka, a function) that allows you to execute additional code around a function call. The docs tell me this is similar to Decorators in Python, if that helps. I can use a wrapper function to check for Failure (and return if so) before doing my stringy checks.

sub adapter( $input ) {
    when $input ~~ Failure { $input }
    default { callsame() }
}

I can now wrap this "adaptor block" function around my validators. If the adapter receives a failure, it simply returns it. Otherwise, callsame() will call the function that it's wrapped around. To wrap my functions, I'll create a Trait which simplifies applying it to my validator functions. I'm calling my trait "validator" but you could name it anything.

sub trait_mod:<is>(Routine $r, :$validator) {
    $r.wrap(&adapter)
}

Alternatively, I can do this all in one step by defining the adaptor function anonymously inside the trait definition.

sub trait_mod:<is>(Routine $r, :$validator) {
    $r.wrap(-> $input {
        when $input ~~ Failure { $input }
        default { callsame() }
    })
}

Once the trait is defined all I need to do to wrap my functions is add two words, is validator, to the function definition.

sub contains-at(Str $s) is validator {
    when not $s.contains('@') {
        fail("No '@' in email address")
    }
}

I'm almost finished, but there one more thing I want to take care of first. Perl 6 is a dynamic language with gradual typing, so I don't need to define types on all my variables... but imagine you were implementing this sort of code in much larger system, with code spread across multiple files. Once a code base gets large enough, enforcing types everywhere helps maintain correctness, but what type will I get back from validate?

The answer is I might get either a Str or a Failure. In the original article, Scott defines a sum type called Result that can indicate either Success or Failure. Perl 6 can do something similar with Junctions. Typically you might use Junctions to compare a value against multiple values... but it can also apply to type subsets. I won't cover Junctions here. You can refer to the docs, or (Perl 6 core dev) Zoffix has a blog post about them here. Here, I'm just going to define a new subset type that type that has a constraint of any(Str, Failure).

my subset Result where Str|Failure;

This new Result sub-type will be my return value, and it will also be the type signature for my adaptor block... and that's about it! My validators have all been adapted to handle two-track input, I'm ready to chain them together,

sub validate(Str $input) {
    $input
    ==> contains-at()
    ==> is-lower()
    ==> not-io()
}

my Result $res = validate('User@host.org');

say "Validation check completed";

Well, that looks quite Functional with a capital F. This syntax uses the Perl 6 feed operator, that allows functions to be chained together (similar to method chaining) which "feeds" the result of the previous function into the next. So now I'm ready to call my validate() function.

Before I do, though, I quickly want to cover what a Failure actually is. Whereas an Exception throws when it occurs, a Failure does not. You can pass a Failure around - or assign it to a variable - but if you try to evaluate it, it will be promoted to an Exception and dump a traceback to where it came from. This allows errors to be handled using standard error handling instead of try/catch.

Due to how a Failure works , I should always get the Validation check completed message regardless of success or failure. Note that my address has a capital letter, so if I simply tried to say $res, the Failure would be promoted to a full-blown Exception and throw... but the whole point of using a Failure is so I can handle it with simple error checking, thus avoiding an ugly traceback.

given $res {
    when Failure { say "FAILURE: $res.exception()" }
    default      { say "SUCCESS: $res"             }
}

I ran it (with the purposely "invalid" address) and go my desired output.

Validation check completed
FAILURE: Email address is not lower-case

Groovy! I changed my input and tested all the fail conditions and the "happy path" and it all worked as I hoped, so I'd call this a success, but there one more thing that bothering me: I have an identical default block duplicated in each validator. I decided to push my adaptor further and get it to handle the default return as well. There might be a better way to do it, but in typical /me fashion, I came up with something that worked and left it that way. Full code incoming.

my subset Result where Str|Failure;

sub trait_mod:<is>(Routine $r, :$validator) {
    $r.wrap(sub (Result $input) {
        when $input ~~ Failure { $input }
        default {
            my $result = callsame();
            when $result ~~ Failure { $result }
            default { $input }
        }
    })
}

sub contains-at(Str $s) is validator {
    when not $s.contains('@') {
        fail("No '@' in email address")
    }
}

sub is-lower(Str $s) is validator {
    when $s ne $s.lc {
        fail("Email address is not lower-case")
    }
}

sub not-io(Str $s) is validator {
    when $s.ends-with('.io') {
        fail("I don't like '.io' domains")
    }
}

sub validate(Str $input) {
    $input
    ==> contains-at()
    ==> is-lower()
    ==> not-io()
}

my Result $res = validate('User@host.org');

say "Validation check completed";

given $res {
    when Failure { say "FAILURE: $res.exception()" }
    default      { say "SUCCESS: $res"             }
}

Forget about the trait, check out my validator functions! Talk about your single responsibility principle. Each one now just has a single when block, and the trait is handling the potential "else" clause. Obviously I could have thrown all of these checks inside a single given block, disable fallthrough with proceed and be done with it... but that's not the point! Remember this is a very simplified model of this concept of two-track functions.

"so... like, a model train set?"

Quiet you! So there you have it. I'm sure smarter people than I can think of ways to improve and extend these ideas, but for me at least, it has been fun using Perl 6 to explore some interesting functional concepts. I can certainly see some benefits to handling error checking this way, particularly if you have a large system that runs various validation checks on data as it passes though your pipeline.

Finally, Scott also advocates for defining your errors in an Enum and then stringifying them later. He makes a good case for this - and it's trivial to do - but I won't do it here, this post is too long already. It's not strictly a functional idea, but a good idea none-the-less. Think of it as your homework assignment.

Using matplotlib in Perl 6 (part 7)

2017-04-11 23:32, Tags: perl python matplotlib

This is Part 7 in a series. You can start at the Intro here

This will be the last entry in this series on using Matplotlib in Perl 6. As I said in the Intro, I was just playing around, found it interesting, and decided to write about it. It also provided a handy excuse to talk about some of the nice features in Perl 6. I never had any intention of creating a full-featured wrapper for Matplotlib, but my hope is that - through this series - I have shown how entirely possibly it is to use Python modules in a transparent manner in Perl 6 via a wrapper.

Last time I took way too long to get to one graph, so I wanna show a few this time. I've covered enough ground that my Matplotlib wrapper can handle basic graphs nicely. Let me just bask in the warmth of how I can churn out a few simple plots without having to fix anything. This time I'm eyeing a few scatter and polar plots. First up is is scatter_demo.

use Matplotlib;
my $plt = Matplotlib::Plot.new;

constant N = 50;
my $x = rand xx N;
my $y = rand xx N;
my $c = rand xx N;
my $s = ( rand xx N ).map( π × ( 15 × * )² );

$plt.scatter( $x, $y, :$c, :$s, :alpha(0.5) );
$plt.show();

Which produces a random variation on this

Jumping straight into another scatter plot, this one is polar_scatter_demo. As you might glean from the name, this one uses a polar projection, as well as the always pleasant-looking HSV color map.

use Matplotlib;

my $plt = Matplotlib::Plot.new;

constant N = 150;
my $r      = ( rand xx N ).map( * × 2 ).List;
my $theta  = ( rand xx N ).map( * × τ );

my $s = $r.map( 200 × *² );
my $c = $theta;

my $ax = $plt.subplot( 111, :projection<polar> );
$ax.scatter( $theta, $r, :$c, :$s, :cmap<hsv>, :alpha(0.75) );

$plt.show()

Which gives me a random variation on this

Nice stuff here.
Using the great list repetition operator covered in Part 2, simple passing of named arguments, mapping an operation over a list of items using real unicode math symbols... and they're nice looking plots too!

But this wouldn't a complete post if I didn't get a curve ball, which I will encounter in the next example, polar_bar_demo. Here's the Python.

import numpy as np
import matplotlib.pyplot as plt

# Compute pie slices
N = 20
theta = np.linspace(0.0, 2 * np.pi, N, endpoint=False)
radii = 10 * np.random.rand(N)
width = np.pi / 4 * np.random.rand(N)

ax = plt.subplot(111, projection='polar')
bars = ax.bar(theta, radii, width=width, bottom=0.0)

# Use custom colors and opacity
for r, bar in zip(radii, bars):
    bar.set_facecolor(plt.cm.viridis(r / 10.))
    bar.set_alpha(0.5)

plt.show()

There's nothing here that looks like it shouldn't work. Last time I compared this series to a predictable movie franchise, but this time there's a plot twist. It's not a method call that threw me this time. Don't get me wrong, I still need to handle that .cm method, so I'll cover that first. Taking a scene from the Part 6 script, I implemented another anonymous class to handle the call to plt.cm

class Matplotlib::Plot {
    method cm {
        class {
            method FALLBACK($name, $idx) {
                $py.run("matplotlib.pyplot.cm.{$name}($idx)", :eval);
            }
        }.new();
    }
    # stuff ...
}

This run looks a little different due to the use of the curlies around the $name variable. This is because had I tried to eval "matplotlib.pyplot.cm.$name(...)" Perl will interpret the $name() as a Perl function

my $f = *.uc;
say $f('hello');    # Result: 'HELLO'

So I need a way to prevent it from being interpreted as a function call. I initially used a a sprintf style statement, but on freenode #perl6, timotimo suggested using curlies instead. This is one of my favourite syntax features of Perl 6 so I can't believe I didn't think of it, and haven't talked about it. Essentially, in Perl 6, any time you see a pair of curlies, it's a code block... even inside a string!

my ( $x, $y ) = ( 100, 3 );
say "$x ÷ $y is { floor( $x ÷ $y ) }, with a remainder of { $x % $y }";
# OUTPUT: 100 ÷ 3 is 33, with a remainder of 1

You can even nest these contructs without Perl getting confused. I should also mention that anytime you see curlies, you should also think: new lexical scope. Any lexical variables declared inside a code block are not visible outside of that code block.

So now the .cm method is working as expected, but that's not the curve ball. No, the real problem occurs later on with this line

my $bars = $ax.bar( $theta, $@radii, :$width, :bottom(0.0) );

for $bars Z @radii -> ( $bar, $r ) { ... }

Perl has a zip() function, but I prefer the versatility of the Z infix operator, because it can also be paired with any other infix operator for easy map-like operations

> (1, 2, 3) Z+ (4, 5, 6)
(5 7 9)
> ( 'A'..'Z' Z~ 1..26 )[^6]
(A1 B2 C3 D4 E5 F6)

However - as far as Perl is concerned - the $bars variable I get back from $ax.bar() is not iterable ie... It is another Inline::Python::PythonObject. I tried to convert it to something iterable via Inline::Python but nothing was working. In desperation, I checked to see if there were any native Python methods on the object that could use to get at the list of things inside.

I modified the python version to run print(dir(bars)) after I got the bars variable and saw a few of the usual suspects: __str__, __repr__, __len__, and others.
Again, maybe there's a better way I could have got at the elements in this thing, but ultimately what worked for me was __getslice__(0, N). This gives me a list slice from the first (0-th) element, up to the N-th element; I could then zip those with the elements in radii. Here's my working code

use Numpl;
use Matplotlib;

my $np   = Numpl.new;
my $plt  = Matplotlib::Plot.new;

# Compute pie slices
my constant N = 20;

my $theta = $np.linspace( 0.0, τ, N, :endpoint(False) );

my @radii = ( rand xx N ).map( * × 10 ).map( *.Num );
my $width = ( rand xx N ).map( π ÷ 4 × * );

my $ax = $plt.subplot( 111, :projection<polar> );
my $bars = $ax.bar( $theta, $@radii, :$width, :bottom(0.0) );

for $bars.__getslice__(0, N) Z @radii -> ( $bar , $r ) {
    my $rgb = $plt.cm.viridis($r ÷ 10);
    $bar.set_facecolor($rgb);
    $bar.set_alpha(0.5);
}

$plt.show()

The only other change I made here was using τ instead of π × 2 because why not. Here's the final plot I will do from the gallery.

But it doesn't feel right to end on an example plot like this. I haven't yet done any plots outside of what exists in the Matplotlib gallery.

In keeping with a Perl 6 kind of flavour, there is curve known as the butterfly curve. The maths behind it is a little beyond me... I tried and failed to get the polar equation in that wikipidia article working, but eventually gave up and got the parametric equation working this way.

use Numpl;
use Matplotlib;

my $np  = Numpl.new;
my $plt = Matplotlib::Plot.new;

my @v = (0, 0.02 ... 72);

sub comp($n) { e ** cos($n) - 2 × cos(4 × $n) - sin($n ÷ 12)⁵ }

my $x = @v.map(-> $t { sin($t) × comp($t) });
my $y = @v.map(-> $t { cos($t) × comp($t) });

$plt.plot( $x, $y );

$plt.show();

Nothing special code-wise here. Just creating a range of values, mapping those values over a given computation, then plotting them.

This looks nice enough, but I couldn't leave it without experimenting with colour. I imagined I could change the colour of the curve based on location, but unless I'm mistaken, it seems Matplotlib doesn't have that functionality. You can, however, mimic that behaviour with a scatter plot and lotsa points... So, I changed a few lines, played with the numbers a bit, and ended up with this

my @v = (0, 0.005 ... 72); # more points!

# stuff...

# $plt.plot($x, $y); # don't need this anymore
my $m = 2;
my $c = ([Z] $x, $y).map(-> ($a, $b) { cos($a × $m) + sin($b × $m) });
$plt.scatter( $x, $y, :$c );

$plt.show();

And now we have this beautifully colourful butterfly curve.

So this is the end of my exploratory series on using Matplotlib in Perl 6. If you're hankering for more, moritz has been covering similar ground over on his blog recently. He's also more knowledgeable than me on all things Perl 6.

I feel confident - and I hope you do too - that given the need to do some plotting, I would reach for Matplotlib without hesitation... even if only for the fact that I'm now more familiar with it than I am with any other plotting library.

If you'd like to play around with my Matplotlib or Numpl modules as they existed after this series, I've put them up in a gist. You're welcome to fork them and do whatever you will.

Thanks for coming on this journey with me.

Using matplotlib in Perl 6 (part 6)

2017-03-23 12:35, Tags: perl python matplotlib

This is Part 6 in a series. You can start at the Intro here.

By this stage this series is beginning to resemble a bad movie franchise. We're back again for another round, featuring the same central characters and themes. The story is essentially the same each time, just the obstacles to overcome change a little. Which is to say, I'm about ready to wrap this series up, but I ran into a few more interesting things so may as well talk about them.

The next plot I tried was another style sheet example, this time it's plot_dark_background. Here's the Python

import numpy as np
import matplotlib.pyplot as plt

plt.style.use('dark_background')

fig, ax = plt.subplots()

L = 6
x = np.linspace(0, L)
ncolors = len(plt.rcParams['axes.prop_cycle'])
shift = np.linspace(0, L, ncolors, endpoint=False)
for s in shift:
    ax.plot(x, np.sin(x + s), 'o-')
ax.set_xlabel('x-axis')
ax.set_ylabel('y-axis')
ax.set_title("'dark_background' style sheet")

plt.show()

Again, most of this stuff is (or looks) straight-forward, but there's a couple new curve balls. First up I want to tackle the easier problem, which is that linspace is being used here in ways my function doesn't handle. At one point, linspace is called with only 2 arguments, and the other time it is called with an endpoint keyword.

Before I even look at that, I want to quickly fix another issue I ran into when playing with my linspace function. I found that when using an irrational number (such as π) as the $end number, something unexpected happened

> Numpl.linspace(0, 3.14159265358979, 7)[5..7]
(2.6179938779914917 3.14159265358979 Nil)
> Numpl.linspace(0, pi, 7)[5..7]
(2.61799387799149 3.14159265358979 3.66519142918809)

I've asked for a Sequence from 0 to π, divided into 7 linear steps. When given a number literal, it works as expected, where the 7th item (index 6) is "pi" and the 8th item is non-existent, ie. Nil. However, when using the built-in π constant, it doesn't consider the 7th item exactly equal to π and the sequence keeps generating infinitely. To fix this, rather than check for equality I will check for "approximate equality" using the =~= operator... but what does "approximate" mean, anyway?

Perl has a global TOLERANCE variable, which by default is 1e-15, or 0.000000000000001. This global can be modified lexically if I want to compare values with a lower tolerance, but the default value works fine for me.

> pi
3.14159265358979
> pi == 3.14159265358979
False
> pi =~= 3.14159265358979
True

So now that's sorted, I moved on to updating the function to act more like the numpy version. I tested in Python and found that when called with 2 arguments, numpy.linspace will default to 50 steps. I modified the function prototype so that $steps is an optional positional just by appending a ? to the parameter. In the multi-sub, I declared $steps to have a default value of 50, and added a named $endpoint parameter... I just need to do something if $endpoint is False. Maybe there's a fancier way to do this, but I just did a simple if-condition. The end result is this

proto method linspace(Numeric $start, Numeric $end, Int $steps?) { * }
multi method linspace($start, $end, 0 ) { Empty  }
multi method linspace($start, $end, 1 ) { $start }
multi method linspace($start, $end, $steps = 50, :$endpoint = True) {
    if $endpoint {
        my $step = ( $end - $start ) ÷ ( $steps - 1 );
        return $start, * + $step ... * =~= $end
    }
    else {
        my $step = ( $end - $start ) ÷ $steps;
        return $start, * + $step ...^ * =~= $end
    }
}

I've spent a lot of time just talking about linspace and I've still got a lot of ground to cover, so I'll just move right into this:

ncolors = plt.rcParams['axes.prop_cycle']

I'm familiar enough with Python to recognise dictionary syntax when I see it.

A dictionary is what Python calls it's associative array type, which in Perl is called a hash. I choose to use all three terms interchangeably.

It seems that rcParams is a class attribute that returns an associative array which can then be subscripted by key. This is actually a unique problem which can't be fully solved using all my old "tricks", and there are a few reasons for that. Sure, I could use Inline::Python::run to return the dictionary... I found that by wrapping the call to matplotlib.pyplot.rcParams with dict in the Python, Inline::Python dutifully returned a Perl hash.

class Matplotlib::Plot {
    # stuff
    method rcParams {
        $py.run("dict(matplotlib.pyplot.rcParams)", :eval)
    }
}

Then I could call that method from my main program, subscript a random key and all seemed ok. (I'm going to use another key axes.xmargin for demonstrative purposes)

say $plt.rcParams<axes.xmargin>    # Result: 0.05 

But! I imagine some other plots might involve changing some of those "params", so how to overcome that? I need my wrapper to be aware if I'm trying to assign a value to that key and run the appropriate Python code. Perl 6 does indeed provide various special methods that allow you to implement an object that behaves like a hash or a list, for example

$obj.foo('bar'); # Calls 'foo' method with argument 'bar'
$obj[2];         # Calls AT-POS method with argument 2 
$obj{'baz'};     # Calls AT-KEY method with argument 'baz'

So all I need to do is implement the relevant methods in my class, which in this case is AT-KEY and ASSIGN-KEY. You can probably guess the latter is called when calling the object like a hash and assigning something to it. Rather than define a "fully-grown" class just to get this functionality, I implemented an anonymous class.

method rcParams {
    class {
        method AT-KEY($key) {
            $py.run("matplotlib.pyplot.rcParams['$key']", :eval);
        }
        method ASSIGN-KEY($key, $value) {
            $py.run("matplotlib.pyplot.rcParams['$key'] = $value");
        }
    }.new();
}

This is just like my style method on Matplotlib::Plot that instantiates a Matplotlib::Plot::Style class, except I've defined the class inside the method anonymously.
A class by any other name would smell as sweet. Now I can do this

$plt.rcParams<axes.xmargin> = 0.08;
say $plt.rcParams<axes.xmargin>    # Result: 0.08

The code inside $py.run() is essentially a string eval, which means, if I pass it a string it will fail because Python will evaluate the $value variable as a bare word. I need to wrap it in quotes, but only if it's a string. This is easily worked around with multiple dispatch. You also might have also noticed I'm not using the :eval option inside ASSIGN-KEY. When I tried to use :eval I got errors; I don't think Python assignments return anything. Typically in Perl, they return the assignment, so I'm going to manually return the provided value to make it more Perlish.

method rcParams {
    class {
        method AT-KEY($key) { ... } # Same as above
        multi method ASSIGN-KEY($key, Str $value) {
            $py.run("matplotlib.pyplot.rcParams['$key'] = '$value'");
            return $value;
        }
        multi method ASSIGN-KEY($key, $value) {
            $py.run("matplotlib.pyplot.rcParams['$key'] = $value");
            return $value;
        }
    }.new();
}

I'd be done here if it weren't for one minor issue. The actual key want from the dictionary - axes.prop_cycle - is itself a dictionary. It seems Inline::Python does not convert this to a Perl hash, and what I get back is an Inline::Python::PythonObject. I hacked away a bit and settled on the first thing that worked.

method rcParams {
    class {
        multi method AT-KEY($key) {
            $py.run("matplotlib.pyplot.rcParams['$key']", :eval);
        }
        multi method AT-KEY('axes.prop_cycle') {
            $py.run(
                "list(matplotlib.pyplot.rcParams['$key'])", :eval
            ).map(|*.values);
        }
        # stuff...
    }
}

Essentially, when I want that particular key, I run exactly the same Python code except that I wrap it in list(). Oddly, wrapping it in dict() didn't help. What I got back was a list of key/value pairs, except, they were all the same key...

[{color => #1f77b4} {color => #ff7f0e} {color => #2ca02c} {color => #d62728}
 {color => #9467bd} {color => #8c564b} {color => #e377c2} {color => #7f7f7f}
 {color => #bcbd22} {color => #17becf}]

So I just run the list through a Perl map and just extract the values. The | there is the short hand syntax for a Slip, a type that "slips" an item into the surrounding list, kind of like the * in Python 3. This is so I get a single list, rather than a list-of-lists.

Boy would you look at the time! I try to keep these posts relatively short but there was so much to talk about. We can finally get to the crescendo of this entry in the franchise... Here's the Perl

use Matplotlib;
use Numpl;

my $plt = Matplotlib::Plot.new;
my $np  = Numpl.new;

$plt.style.use('dark_background');

my ( $fig, $ax ) = $plt.subplots();

constant L = 6;
my @x = $np.linspace(0, L);

my $ncolors = $plt.rcParams<axes.prop_cycle>;

my @shift = $np.linspace(0, L, +$ncolors, :endpoint(False) );

for @shift -> $s {
    my $wave = @x.map(-> $x { sin($x + $s) });
    $ax.plot($@x, $wave, 'o-' );
}

$ax.set_xlabel('x-axis');
$ax.set_ylabel('y-axis');
$ax.set_title("'dark_background' style sheet");

$plt.show();

The only thing here that I don't think I've covered before is that +$ncolors in my second call to linspace. Here, $ncolors is a list, but I can't just pass it to linspace, which expects an Int as it's 3rd positional argument... so I manually coerce it to an Int by prefixing it with +.

Hey, wait a minute! I didn't even need the values in rcParams<axes.prop_cycle> at all! I just need it's number of elements. I could just as easily replace +$ncolors with a literal 10 and it would do the same thing. Still, I don't consider all that work a waste. It may well come in handy for me later... or maybe you. Anyways, here's the resulting graph

Well that's nice, isn't it... Even if it took a while to get here. As stated at the top of this post, I am almost ready to wrap up the series, but want to cover a few more things.

To be continued...

Using matplotlib in Perl 6 (part 5)

2017-03-10 23:24, Tags: perl python matplotlib

This is Part 5 in a series. You can start at the Intro here.

All the graphs I've done so far are using the standard Matplotlib style. In some of them, I might have specified some colour here or there, but Matplotlib also provides "style sheets" for changing the overall look and tone of a graph. That's what's happening here.

import matplotlib.pyplot as plt
plt.style.use('bmh')

It looks like style is another sub-package to matplotlib.pyplot, which then calls a method called use. In a similar vein to how I solved my issues with mlab, I'll need to create a new Matplotlib::Plot::Style class and also make it possible to instantiate that class from a pyplot object. I figured I could sort that out later... First I need to actually get the graph working.

First things first, I need to define a plot_beta_hist function that's called several times later.

from numpy.random import beta
# stuff...
def plot_beta_hist(ax, a, b):
    ax.hist(beta(a, b, size=10000), histtype="stepfilled",
            bins=25, alpha=0.8, normed=True)

We've got another numpy.random function here. This time it's beta which is... err, something to do with... um, probability distributions... parametrized by, uh... shape parameters... *blink*
Look, Wikipedia's over there, you can read all about it. I checked RosettaCode and found nothing so I tried to look up an algorithm, but the sites I found all used a lot of fancy symbols. This stuff was way over my head.

I really wanted to avoid making another wrapper for numpy, so I turned to my faithful stead, Perl 5. I found Math::Random on MetaCPAN which has a random_beta function!

For those unaware, Perl 6 is a different language to Perl 5 with a completely different runtime, so you can't natively use Perl 5 modules. However - just like Inline::Python - there is also an Inline::Perl5 module in the Perl 6 ecosystem. It's also made by Stefan Seifert (thanks Stefan!), and it's also much more developed and stable... to the point where you can import pretty much import any Perl 5 module and just use it as if it were a native module. No need for creating wrappers. It's as easy as this.

use Math::Random:from<Perl5> <random_beta>;

I don't even need to say use Inline::Perl5, I just use Math::Random and tell Perl 6 that this it's a Perl 5 module. Inline::Perl5 is automatically imported and does the rest. In this particular case, I'm also importing the random_beta function I need... And that's it!
Oh, and I'm also making use of that < word quoting > syntax covered way back in Part 2.

Perl has always been known as a great glue language, and here we see Perl 6 not simply spawning processes and parsing the output, but actually communicating directly with Python and Perl 5 and combining them to form one big delicious langwich! It really is a thing of beauty.

I converted the rest of the code (sans style sheet) and ran it. A wild graph appears! What the...

This is why you should read documentation. In the original Python, numpy.random.beta takes it's α and β values first followed by the size. However Math::Random::random_beta takes a size value first, followed by α and β. Serves me right. I swapped the arguments the right way around. Here's the final code (again, sans style sheet).

use Matplotlib;
use Math::Random:from<Perl5> <random_beta>;

my $plt = Matplotlib::Plot.new;

#$plt.style.use('bmh');

sub plot_beta_hist($ax, $a, $b) {
    my $beta = random_beta( 10000, $a, $b );
    $ax.hist(
        $beta,
        :histtype('stepfilled'),
        :bins(25), :alpha(0.8),
        :normed(True)
    );
}

my ( $fig, $ax ) = $plt.subplots();
plot_beta_hist( $ax, 10, 10 );
plot_beta_hist( $ax,  4, 12 );
plot_beta_hist( $ax, 50, 12 );
plot_beta_hist( $ax,  6, 55 );
$ax.set_title("'bmh' style sheet");

$plt.show()

The only real difference in my plot_beta_hist function is I've assigned the result (a list) to a scalar variable $beta before passing it to the hist method. On Reddit, raiph and I discussed other ways to pass scalar variables to Python.

You could wrap the call to random_beta in a scalar container

$ax.hist(
    $( random_beta( $a, $b, 10000 ) ),
    # other args...
);

or you could assign it to the anonymous scalar so it gets evaluated to a scalar before being passed to the function

$ax.hist(
    $ = random_beta( $a, $b, 10000 ),
    # other args...
);

There's more ways, and if you're playing along at home the choice is yours, but I like the readability of mine. Whichever one is used, the result is finally something that looks close to the example in the gallery.

Alright, we got a graph, but here's a quick life lesson. Just because someone presents data in a pretty graph doesn't mean it's true. This is a prime example of a misleading graph. It clearly says atop the graph it's using a 'bmh' style sheet. Lies! So, lets get back to that.

Similar to what I had done in Part 4, I created another class wrapping matplotlib.pyplot.style and also made it possible to instantiate this class when calling the style method on a Matplotlib::Plot object.

class Matplotlib::Plot::Style {
    method FALLBACK($name, |c) {
        $py.call('matplotlib.pyplot.style', $name, |c)
    }
}

class Matplotlib::Plot {
    method style {
        Matplotlib::Plot::Style.new
    }
    # stuff ...
}

Problem solved, right? Nope... I got an error back from Python saying name 'use' does not exist which is typically something Python says when you haven't imported something before trying to use it, but that didn't look like the case here. Additionally, while Inline::Python is kind of amazing for what it does, it's not really documented as it's still under development. I was really only aware of 2 methods: call and run.

The call method is literally for calling class methods. It takes 2 mandatory arguments: a package, and a method name; optional arguments can also be called. The run method is more for running arbitrary bits (ie, not class methods). It just takes one mandatory argument: some python code as a string. I managed to get it working using run instead. Again, my Python ignorance is showing here, but it seems style.use is not a method call, but probably an attribute or something.

I added a use method to my Matplotlib::Plot::Style class and called run with the relevant python code. While researching pyplot.style I found it also has an available attribute that returns a list of all the style sheets! That could come in handy, so I added it as well. For both these uses of run, I'm passing the eval option, which tells Inline:Python to return values from the evaluated python code.

class Matplotlib::Plot::Style {
    method use($name) {
        $py.run("matplotlib.pyplot.style.use('$name')", :eval)
    }
    method available {
        $py.run("matplotlib.pyplot.style.available", :eval)
    }
    method FALLBACK($name, |c) {
        say $name;
        $py.call('matplotlib.pyplot.style', $name, |c)
    }
}

The python code in is in double-quotes so that variables get interpolated, but I also had to put single-quotes around the argument for it to work... but it did work, and that's all I really care about. Look at this thing

So there we have it... One more thing! Remember we can get a list available styles. We can use this to pick a random style to display each time.

my $name = $plt.style.available.pick;
$plt.style.use($name);
# stuff...
$ax.set_title("'$name' style sheet");

So that's part 5. I didn't think I would write this much, but I'm still having fun here.

To be continued...

Earlier