Refactoring deeply nested case statements

What is the best way to refactor code like this?

def answer(question) do
    case exact_match(question) do
      nil -> 
        case proximity_match(question) do
          nil -> 
            case fuzzy_match(question) do
              nil -> 
                case wild_card_match(question) do
                  nil -> 
                    case spell_check_match(question) do
                      nil -> 
                        case special_match(question) do
                          nil -> 
                            case free_match(question) do
                              nil -> nil
                              a -> a
                            end
                          a -> a
                        end
                      a -> a
                    end
                  a -> a
                end
              a -> a
            end
          a -> a
        end
      a -> a
    end
  end

Well in this case if we’re dealing with just truthy and falsey values a very very simple change is to just use ||

def answer(question) do
  exact_match(question) ||
    proximity_match(question) ||
    fuzzy_match(question) ||
    wild_card_match(question) ||
    spell_check_match(question) ||
    special_match(question) ||
    free_match(question)
end

Alternatively you could use a list of functions and Enum.find

def answer(question) do
  tries = [
    &exact_match/1
    &proximity_match/1
    &fuzzy_match/1
    &wild_card_match/1
    &spell_check_match/1
    &special_match/1
    &free_match/1
  ]

  Enum.find(tries, fn fun -> fun.(question) end)
end
2 Likes

Or a with:

with\
  nil <- exact_match(question),
  nil <- proximity_match(question),
  nil <- fuzzy_match(question),
  nil <- wild_card_match(question),
  nil <- spell_check_match(question),
  nil <- special_match(question),
  nil <- free_match(question),
  do: nil
6 Likes

Yeah, I always try to use with in cases like this. The readability is excellent.

Thanks for all the good answers!

This is not equivalent! Lets assume the result of exact_match(question) is false.

1 Like

Good catch! In my defense I did nuance my reply with " if we’re dealing with just truthy and falsey values ". Nonetheless the with answer is likely the best, and it’s certainly the most identical.

1 Like