Galaxy brain: what if your functions are data.
Let’s take filtering a list of integers as an example. We could define Filter as a protocol like so:
defprotocol Filter do
defstruct [:collection, :predicate]
def apply(collection, predicate)
end
Now let’s encode our predicate as data:
defprotocol IsOdd do
defstruct filter: &__MODULE__.filter/1
def filter(a)
end
defimpl IsOdd, for: Integer do
def filter(a) do
require Integer
Integer.is_odd(a)
end
end
Now Lets implement the zip for a list
defimpl Filter, for: List do
def apply(list, predicate) do
Enum.filter(list, fn x -> predicate.filter.(x) end)
end
end
All of that lets us do this:
Filter.apply([1,2,3], %IsOdd{})
Which gets us close. We now just need to capture all of that in its own struct. We’ll define a general function application protocol:
defprotocol Function do
def apply(function)
end
Then implement if for Filter:
defimpl Function, for: Filter do
def apply(filter) do
Filter.apply(filter.collection, filter.predicate)
end
end
Now we can create our Filter function as a data structure, and as long as where ever we are sending it has the right protocol implementations we can consume it:
%Filter{collection: [1,2,3], predicate: %IsOdd{}}
|> Function.apply( )
What even more interesting is because it’s all protocols each dimension of the filtering problem is extensible. Filtering a collection has 3 dimensions to the problem, the collection being filtered, the items in the collection and the predicate that determines whether something stays in the collection.
Lets now make it so that we can filter on Decimals inside lists:
defimpl IsOdd, for: Decimal do
def filter(a) do
Decimal.positive?(a)
end
end
%Filter{collection: [Decimal.new("1"), 2, 3], predicate: %IsOdd{}}
|> Function.apply( )
Okay and now let us filter on maps as well as lists:
defimpl Filter, for: Map do
def apply(map, predicate) do
Enum.filter(map, fn {k, v} -> predicate.filter.(v) end)
end
end
Function.apply(%Filter{collection: %{a: 1, b: 2, c: 3}, predicate: %IsOdd{}})
Disclaimer, I just find this interesting I have no idea whether it’s a good idea to actually use.