DEV Community

Cover image for Functional Programming with Raku
Rawley Fowler
Rawley Fowler

Posted on

Functional Programming with Raku

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
Enter fullscreen mode Exit fullscreen mode

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);
    }
Enter fullscreen mode Exit fullscreen mode

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)))) }
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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))
Enter fullscreen mode Exit fullscreen mode

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 };
Enter fullscreen mode Exit fullscreen mode
Whatever Code
my &add = * + *;
Enter fullscreen mode Exit fullscreen mode
Pointy block
my &add = -> $a, $b { $a + $b };
Enter fullscreen mode Exit fullscreen mode
Anonymous sub
my &add = sub ($a, $b) { $a + $b };
Enter fullscreen mode Exit fullscreen mode

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';
    }
}
Enter fullscreen mode Exit fullscreen mode

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!
Enter fullscreen mode Exit fullscreen mode

In Humming-Bird when you declare routes like:

get('/', -> $request, $response {
   $response.write('Hello World!');
});
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Take care! Raku rocks!

Top comments (0)