The tale of the K combinator and the alias method
Tags: ruby
Published: 2021-03-31 03:05:34

The other day after a good ol' code cleaning, the file I was working on seemed too long and repetitive. The reason to that was because of a set of four *different* methods doing the exact same thing: returning an argument.

More context:

The goal was to create an export of a collection.
For this I had two type of files:

### application_exporter.rb

`class ApplicationExporter`

It has all the necessary methods to create a .xlsx file. It's also in charge of grabbing all the records for a given Model and apply to it a method to prepare these records (check the policy, apply filters, ...).
It looks like this:

``` ruby
def prepare_exported_data
  @data = model_name
  @data = apply_policy_to_data(@data, export_creator)
  @data = apply_filters_to_data(@data, export)
  @data = apply_scope_to_data(@data, export)
end

def model_name(name)
  # takes the model name and returns Model.all in an array
end

def apply_policy_to_data(data, user)
  # apply the policy to the data to make sure the user won't
  # export data they shouldn't have access to
end

def apply_filters_to_data(data, export)
  # if the user asked to see only a certain type of data, 
  # give them only what they asked
end

def apply_scope_to_data(data, export)
  # in case there is a scope applied, let's apply it to the 
  # collection
end
```

### model_exporter.rb

`class ModelExporter < ApplicationExporter`

This is the file for the Model records (it can be User, Posts,...). As the dependent of ApplicationExporter, it inherits from it all its methods.
This class will have all the necessary methods to fill the export, with the necessary attributes, and will define the columns, the headers, etc...

If I want something basic like exporting all the records of a given model, the only method it needs is the one defining the columns of the spreadsheet.


### So what's the problem here?

My problem was that ApplicationExporter builds the export with a collection of records from the database by using the `model_name` given to it when the process is fired. But this time the data came from a hash in the code base and only one attribute had to be fetched from the database.
That means many methods from ApplicationExporter were useless and I had to override them in the class ModelExporter.

First I had to create the collection of data I needed. For that I overrode the #model_name method:

``` ruby
def model_name
  ::VERY_IMPORTANT_HASH.map do |key, values|
    {
      name: key,
      value: whatever_info_in_the_hash_for_this(key)
      default_value: Model.find_by(name: key)&.default_value
    }
  end
end
```
Here, name and value are taken from the hash, but the default_value comes from the database. It returns an array (thanks to #map) which is great because this is the data type I need for the exporter process to work.

Now comes the moment when the method `#prepare_exported_data` that I have shown above is fired, *and also when the title of this post will finally make sense...* (bear with me :) ) 

For this specific export I don't need any policies or filters applied, even if they exist. That means that every single method fired by `#prepare_exported_data` has to return the collection without modifying it.

I ended up having to override all the methods this way:
``` ruby
def apply_policy_to_data(data, _user)
  data
end

def apply_filters_to_data(data, _export)
  data
end

def apply_scope_to_data(data, _export)
  data
end
```

You can notice the underscores on all the arguments that doesn't concern the data. That means: "I know I am supposed to take 2 arguments, but I won't care about the second one <3".
Then, the only thing all the methods do is to return the `data` passed as argument.

So how to avoid this 'repeat' mode? For that I used a concept from Calculus: Kestrel Combinator, also called K Combinator.
It is a function taking two arguments and returning only the first one, considered as a constant.
It is as simple as that!

Let's take a look at how to use it in my case:

``` ruby
def const(collection, _)
  collection
end

alias_method :apply_policy_to_data,  :const
alias_method :apply_filters_to_data, :const
alias_method :apply_scope_to_data,   :const
```

There is nothing spectacular here: I achieved to have a cleaner and lighter code by using a little trick. 
It is just one example about how to use a mathematic concept in code.

There are other kind of combinators, you can learn about them by taking a Calculus class... or reading [this great article with Javascript examples](https://www.willtaylor.blog/combinators-and-church-encoding-in-javscript/). And if you had to use one of them, please share!