Writing some in Elixir

Published October 13, 2018 by Toran Billups

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.


Buy Me a Coffee

Twitter / Github / Email