Get rid of the :ok in a tuple of a function successful return

I’m doing web scrapping where I generate a list of smoothies.

Here is the final function :

   def get_smoothies_recipe() do
     smoothies =
         get_smoothies_url()
         |> get_smoothies_html_body()
         |> Enum.map(fn body ->
           %{name: name, ingredients: ingredients, directions: directions} = %{name: get_smoothie_name(body), ingredients: get_smoothie_ingredients(body), directions: get_smoothie_directions(body)}
         end
         )

     {:ok, smoothies}
   end

My problem is that my functions get_smoothie_name, get_smoothie_ingredients and get_smoothie_directions all return a tuple {:ok, result} .

Hence each element of my final smoothies array looks like so:

  %{
    directions: {:ok,
     ["Cut banana into small pieces and place into the bowl of a blender.  Add the soy milk, yogurt, flax seed meal, and honey.  Blend on lowest speed until smooth, about 5 seconds.  Gradually add the blueberries while continuing to blend on low.  Once the blueberries have been incorporated, increase speed, and blend to desired consistency.\n                            "]},
    ingredients: {:ok,
     ["1 frozen banana, thawed for 10 to 15 minutes",
      "1/2 cup vanilla soy milk", "1 cup vanilla fat-free yogurt",
      "1 1/2 teaspoons flax seed meal", "1 1/2 teaspoons honey",
      "2/3 cup frozen blueberries", "Add all ingredients to list",
      "Add all ingredients to list"]},
    name: {:ok, "Heavenly Blueberry Smoothie"}
  }

I want to get rid of those :ok in my final return statement. How can I do that ?

with {:ok, directions} <- get_smoothie_directions(body),
     {:ok, name} <- get_smoothie_name(body),
     {:ok, ingredients} <- get_smoothie_ingredients(body) do
  %{name: name, ingredients: ingredients, directions: directions}
end
4 Likes

exactly what I was looking for. thank you very much @LostKobrakai !

Why do those functions return an ok tuple, instead of just the result? One simple approach, if those functions are code you own, and you never care about error conditions, then have them just return the raw value. Another common pattern is to have bang versions of the functions (like get_smoothie_name!) that only return the raw result.

It really all boils down to whether those functions can fail, and whether you can do something reasonable if they do. If you can’t really do anything, then letting the process fail may be the best outcome.

9 Likes

I see @gregvaughn got it !

I just thought it was good practice to always write my functions with an :ok or :error atom.
But what you say make completely sense.

The functions in my particular case should never fail, if there are no name/ingredients/directions found, it returns an empty array.

so I should just get rid of this :ok from the return statement of each function

1 Like

Out of curiosity, are you using a library to do web scraping? Or by your own code?

2 Likes

Hi @maz ! I’m using HTTPoison to fetch pages and Floki to parse HTML :slight_smile:

Yes, it’s all about errors. Isn’t it always? :wink:

2 Likes

I’m not sure if the solution of @LostKobrakai is correct. What happens if one of smoothie would have :error instead of :ok?

Therefore I would use following result library which can handle this error state and the code could look like:

def get_smoothies_recipe() do
  smoothies =
    get_smoothies_url()
    |> get_smoothies_html_body()
    |> Enum.map(fn body ->
      [get_smoothie_name(body), get_smoothie_ingredients(body), get_smoothie_directions(body)]
      |> Result.fold()
      |> Result.map(fn [name, ingredients, directions] -> %{name: name, ingredients: ingredients, directions: directions}
      end)
    end)
    |> Result.fold()
end
2 Likes

Great, nice and simple.