Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I've seen that recommendation often but I still dislike it, since it falls apart whenever you need to do the same logic on multiple pieces of data. It only makes sense if every single with-clause is doing something hugely different.

A simple example:

    with {:ok, cleaned_first}  <- sanitize(data.first),
         {:ok, cleaned_middle} <- sanitize(data.middle),
         {:ok, cleaned_last}   <- sanitize(data.last), 
         {:ok, final}          <- combine(first, middle, last) do:
      {:ok, "Name is" <> final}
    else
      {:error, :illegal_character} ->
        {:error, :illegal_first_name_or_middle_name_or_last_name}
      other ->
        other
    end
The above sucks because it's not clear which data was bad or which step failed.

So let's talk about the alternatives...

__________________

First, the "Lots Of Methods" approach, where you just add as many different private methods as you need, where each one works almost the same except for a distinctly different error:

    {:ok, cleaned_first}  <- sanitize_first(data.first),
    {:ok, cleaned_middle} <- sanitize_middle(data.first),
    {:ok, cleaned_last}   <- sanitize_last(data.first),
I feel that sucks because it's tedious, error-prone boilerplate, even if they're just wrappers around a sanitize/1 that does the real work. Also any new error-atoms being introduced are scattered down the file rather than near where you might want to match/test them.

__________________

Second, the "Augment One Method With Causal Data" version:

    {:ok, cleaned_first}  <- sanitize(data.first, :phase_first),
    # ... else ...
    {:error, :illegal_character, :phase_first} -> {:error, :illegal_first_name}
This is a marginal improvement, but we're still contaminating methods like "sanitize" with junk they don't actually need to know in order to do so their job, passing a piece of opaque data down and up the stack unnecessarily.

__________________

Third, what if we carefully isolate the concerns/complexity to the with-statement... Hey! We're right back to where we started!

The "Augment The With Clauses" approach, which I argue is least-bad:

    {_phase, {:ok, cleaned_first}}   <- {:phase_first,  sanitize(data.first)},
    {_phase, {:ok, cleaned_middle}}  <- {:phase_middle, sanitize(data.middle)},
    {_phase, {:ok, cleaned_last}}    <- {:phase_last,   sanitize(data.last)},
    # ... else ...
    {:phase_first,  {:error, :illegal_character}} -> {:error, :illegal_first_name}
    {:phase_middle, {:error, :illegal_character}} -> {:error, :illegal_middle_name}
    {:phase_last,   {:error, :illegal_character}} -> {:error, :illegal_last_name}


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: