In part 19 of my Elixir journey I wanted to show how you might implement the Elixir equivalent of Array.prototype.some from javascript. This functionality can also be found in Elixir as `Enum.any`
Some
Today I'll be hand rolling `some` and using it to confirm a number exists in a list.
Enum.any?([1, 2, 3], &(&1 == 2))
The interface for this `some` function is fairly straight forward. The first parameter is a list to enumerate. The second parameter is a function we apply to each item in the list. If this function evaluates as truthy the `some` function returns immediately with the value `true`. The return type is a boolean.
defmodule MyEnum do
def some([], _func), do: false
def some([head | tail], func) do
case func.(head) do
true ->
true
false ->
some(tail, func)
end
end
end
The first function clause acts as our starting point returning false. We include 2 parameters here only to ensure we pattern match correctly. The last parameter `_func` is not needed so to avoid the compiler warning we prefix it with an underscore.
In the second function clause we invoke `func` with the `head` of the list and when truthy we bail out of `some` altogether returning the value `true`. If the result of `func` is not truthy we continue searching down the list by invoking `some` with the `tail` and `func`.
MyEnum.some([1, 2, 3], &(&1 == 2))
We can calculate the return value from `some` by walking through the steps our program will execute in reverse. It's worth mentioning that each time the `some` function calls itself, it adds to the stack. And when the `some` function returns it will pop off the stack (hence the reverse order).
true # some([2, 3])
(false) true # some([1, 2, 3])
If we search for a number that does not exist we expect to get `false` as our return value.
MyEnum.some([1, 2, 3], &(&1 == 4))
false # some([])
(false) # some([3])
(false) # some([2, 3])
(false) # some([1, 2, 3])
Implementing `some` was a lot like `filter` in that we used the `case` control flow structure. All good with me as I could use some repetition but next time I'll explore something radically different.
Update: special thanks to Simon Olsberg for providing a much more concise version of `some`. The original solution I had was verbose to say the least.