Evan Now


I commute via public transit nearly every day. I've done it since moving to Portland. I didn't mind having an hour commute (I did that for a few years), but there was a period of time when I needed to take both a bus and train in order to get to/from work. I did not like getting off work, waiting for the train, and then sometimes having to wait 15-20 minutes to transfer for the next bus.

I didn't like using Trimet's mobile website, their trip planner, nor did I like any of the publicly available apps. At the time, Google maps did not have accurate data and my GPS would take a while to lock onto my location.

I wanted something that would immediately display results that were catered to me. So I brushed up on my Java and wrote an Android app that would help me plan my trips better.

I both live and work close to a MAX stop now, but on a daily basis, I still want to know the answer to my question "When is my next train?".

This kind of evolved into something similar to what Alphabet calls "Google Now." On top of personalized train times, I also can view the hourly weather forecast so I know if it's going to rain, and if there's a Timbers game today (I live near the stadium).

Sometime down the road, I might include stocks and my Google Calendar


Elixir: with

Here's some code that I initially wrote for my Now page. It fetches weather data, parses it from JSON into Elixir maps, then checks if I have an "hourly_forecast" before converting the data into something more usable by me.

  defp fetchWeather do
    resp = HTTPotion.get("http://api.wunderground.com/api/" <> wunder_key <> "/hourly/q/OR/Portland.json")
    case Poison.decode(resp.body) do
      {:ok, resp2} ->
        if Map.has_key?(resp2, "hourly_forecast") do
          {:ok, resp2["hourly_forecast"] |> convertForecast }
         else
          {:error, "could not fetch forecast data" }
        end
      _ -> {:error, "could not fetch forecast data" }
    end
  end

It started off easy-to-read when it was just a few lines, and then got relatively ugly due to conditionals and copy/paste. Handling exceptions came at the cost of readability. Here's the rewrite, doing almost the same thing as above:

  defp fetchWeather do
    resp = HTTPotion.get("http://api.wunderground.com/api/" <> wunder_key <> "/hourly/q/OR/Portland.json")
    with {:ok, decoded} <- Poison.decode(resp.body),
         {:ok, hourly} <- Map.fetch(decoded, "hourly_forecast"),
         do: {:ok, hourly |> convertForecast}
  end

With with (included in Elixir 1.2), we focus on the "happy path". If we encounter an error anywhere along the way, whether it's from fetching data, parsing the JSON, or if the hourly_forecast data does not exist in the map, then we exit out with an {:error}. The code that calls fetchWeather will handle the :ok or :error response.

I'm reminded of all the Monad and Railway-oriented programming stuff I read when trying to get a deeper learning of F#. In my opinion, this feature of Elixir is very intuitive, easy-to-read, AND extremely useful. This language excites me with just how efficient I can be with it.