Folding Erlang Records
When developing the new RabbitMQ Delayed Exchange plugin I had the problem of extracting values from deeply nested records like the one created for message deliveries in RabbitMQ.
Imagine you have the following record from which you need to access
the headers
filed of the innermost #'P_basic'
record.
Of course we could have pattern matching on a function header to extract the innermost value, like this:
Besides from the point that doing this kind of destructuring gets a bit hairy, what would happen if at some point we want to just access all the content properties? Or just the basic_message?
To solve this I decided to have a function to access each specific property from each record, ending up with these functions:
To use these functions we could call each one in succession and then return the headers:
Now in the above situation, what would happen if the field we are
trying to access from the record is undefined
? One solution would be
to add an extra clause to each of the get_*
functions where we
handle the undefined case:
While that would work, it seems to me there should be a better
solution. Enter foldl
.
With lists:foldl/3 we can do the following:
When calling this fold using the current record as the accumulator and a list of functions as the things to apply on each iteration of the fold. Each successive call will be able to go deeper in the record structure until it finds the headers we are looking for.
We still have the problem of what happens when a record property we
try to access returns undefined
. Thanks to Erlang’s pattern matching
on function headers, we can easily address that:
With this last version of the function, now we can handle the
undefined
case but we still have the problem that the fold doesn’t
short circuit the first time it finds and undefined
value. Let’s add
that:
Here we added a throw()
to short-circuit the fold and a catch
to
handle that. In the successful case, catch would return the header we
want to access, for all other cases undefined
will be returned as
soon as a record field is undefined.
By using fold and a list of funs, we can decide how deep in the record we want to go just by providing the right list of functions.
I hope this idea of folding Erlang records is useful to you, which BTW, looks an awful lot like composition in the Maybe Monad.
Here’s a gist with the code examples: fold_example.erl.
Credits
Photo Credit Ran Yanviv Hartstein. License CC BY 2.0.