DEV Community

Cover image for Functional Programming with Raku
Rawley Fowler
Rawley Fowler

Posted on

2

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!

Heroku

Simplify your DevOps and maximize your time.

Since 2007, Heroku has been the go-to platform for developers as it monitors uptime, performance, and infrastructure concerns, allowing you to focus on writing code.

Learn More

Top comments (0)

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up