Raku is a great language for functional programming, it has support for anonymous functions (blocks), partial application via assuming
, and much more.
In this post I hope to outline how I use Raku as a functional (object-oriented) programming language similar to how I'd write a language like Scala.
The compose operator
In Raku o
is an operator (a binary function). Which means we can compose two functions together to create a new one. This lets us build really cool abstractions over our functions. For example we can use it to create functions with hidden side-effects:
my $logger = -> $m { say $m; $m };
my $add-five = -> $x { $x + 5 };
my $add-five-and-log = $add-five o $logger;
say $add-five-and-log(25); # Prints 25, then prints 30
Note that the composition occurs backwards in a way, $add-five o $logger
is equivalent to -> x { $add-five($logger(x)) }
. Pretty neat right!
This also allows us to simply reduce arrays of functions into a single function, in the Humming-Bird source-code, you can see that in practice for "advice" the end-of-routing hooks:
method !add-route(Route:D $route, HTTPMethod:D $method --> Route:D) {
my &advice = [o] @!advice;
my &cb = $route.callback;
my $r = $route.clone(path => $!root ~ $route.path,
middlewares => [|@!middlewares, |$route.middlewares],
callback => { &advice(&cb($^a, $^b)) });
@!routes.push: $r;
delegate-route($r, $method);
}
This method on the Router class adds a route object to the context of the application, you'll notice the first line of the method, we apply o
in a reduction to produce a single function from the advice array. @!advice
being all the advice on this router. Advice are simply functions that take a response, and return a response. So you can imagine how the composition operator is layering all of them together like:
-> x { advice-four(advice-three(advice-two(advice-one(x)))) }
Then, to call the advice all we have to do is pass a response to the composition, and it will handle the rest.
Assuming
Assuming is a method derived from the Code
class, and it allows us to perform partial-application on a function. Partial application is very simple, it means to call a function with less arguments than it requires, which in-turn returns a new function that contains the parameters you've already supplied. For example if we have an add
function that takes two numbers, we could partially apply it like so:
my $add = -> $x, $y { $x + $y };
my $add-five = $add.assuming(5);
say $add-five(10); # prints 15
say $add-five(5); # prints 10
The result of calling .assuming(5)
on the add
function is a new function that looks like: -> $y { 5 + $y }
.
This is a really neat feature that lets us create what I like to call 'stateless-state', meaning we can add state to our functions without actually exposing it to the consumer of the function.
A fairly complicated, yet elegant example of using .assuming
in the wild is Humming-Birds middleware system:
my &composition = @!middlewares.map({ .assuming($req, $res) }).reduce(-> &a, &b { &a({ &b }) });
&composition(&!callback.assuming($req, $res))
We have to map all middleware to partially-apply the request and response objects that will be used throughout the request chain, then reduce to a single function that will take another function. Then finally, call the composition with the callback provided by the user. This is what allows middleware to have the $request, $response, &next
parameters. Calling &next()
is simply calling the next partially applied function in the chain!
Anonymous functions (blocks)
Raku is one of the only languages I know that has more than one way to declare an anonymous function (aka lambda). In Raku we have pointy-blocks (my favorite), normal blocks, WhateverCodes and anonymous sub-routines.
Typically, in my experience, you'll be fine with a normal block for most things, unless you want to strictly define your parameters, then it's best to use a pointy-block or anonymous sub-routine. If you need to explicitly return i.e short-circuit you'll probably want to use an anonymous sub, otherwise a pointy block will do.
Here's the same function add
written using each type of block:
Normal block
my &add = { $^a + $^b };
Whatever Code
my &add = * + *;
Pointy block
my &add = -> $a, $b { $a + $b };
Anonymous sub
my &add = sub ($a, $b) { $a + $b };
You can use the &
sigil to declare functions stored in variables in a more 'Raku-ish' way, this will tell other developers that the variable is a Callable
.
You can pass these anonymous functions to other functions, this is used fairly-extensively in the realm of reactive-programming. whenever
registers a callback with an anonymous function (block or pointy block):
react {
whenever Supply.interval(1) -> $second {
say 'Current second: $second';
}
}
In Raku's standard library, for example, a for loop takes a block or point block as an argument, you probably never thought about it like that before right?
for [1,2,3,4] -> $x { say $x }; # That block can be thought of as a lambda!
In Humming-Bird when you declare routes like:
get('/', -> $request, $response {
$response.write('Hello World!');
});
In the background that block is taken in by the get
function then registered as what to call when the router receives a request.
Overall, Raku has some really interesting and usable functional patterns. I personally am a big fan of partial application and the composition operator. It is really fun as-well!
To finish off I'd like to leave a fun example, see if you can predict the result:
([o] [-> $a { $a + 1 }, -> $a { $a + 1 }])(2).say
Take care! Raku rocks!
Top comments (0)