Haskell Library for Forecast.io
Two of the joys of working with Haskell is how quickly code can be prototyped and how quickly it can be (safely) refactored. What follows is a quick tale of how a library for the Forecast.io weather API came to be. I’ve also released it on Hackage and github.
Last month, I ran into some trouble with my weather app. I noticed one day that I started to very frequently get high latency alerts. After examining my reports, I saw that I had numerous requests in the 30-60 second response time. After doing some more digging, I discovered that nearly every request I was making to retrieve weather forecasts was timing out.
My app’s foundation is reporting weather. Needless to say, this was bad.
I contacted the maintainers of the third-party weather API from which I get forecast data. They told me their servers were currently overloaded and I’d need to implement a caching solution.
This problem was effectively killing my app. I was facing a dilemma:
- My backend already aggressively caches weather forecasts.
- Weather is notoriously fickle and changes often.
- I was already starting to receive bad reviews of the app because of the outage.
- My reports showed that the third-party service had effectively been unavailable for over 48 hours.
Solution
The only viable solution was to replace the weather forecasting service that stood as the backbone of my app.
Within 8 hours, I had fully implemented and deployed the solution to my users. (Haskell, you’re my favorite. Shhh, don’t tell my wife!)
I switched over to using Forecast.io. Their API docs are quite detailed, their service has been shown to be very responsive, and the accuracy of my forecasts has improved (all for a deliciously low price; they’ve got a great pricing model). The only problem is that I couldn’t find a “pure Haskell” library already available to talk to their service. Learning details of their service and building this library is what took most of that 8 hour time to deployment.
The really brilliant part is in being able to completely replace a core piece of an app within a single day and having complete confidence in the result. It’s moments like this that make you really happy to be working with such a powerful language as Haskell.
The Open-Source Library
I’ve taken the relevant bits out of my server code and am now open-sourcing it as a library for others to build upon. The new library is very simple. One module that defines data types for the corresponding JSON types returned by the API. The other module defines a helper endpoint builder that can form a Forecast.io URL for calling into their API.
Downloading
I did intentionally leave out a download function, as my assumption would be that most users of the library would probably want their own implementation or to hook into another library for HTTP requests. A basic implementation could look like this:
import qualified Data.ByteString.Lazy.Char8 as LB import Data.Maybe import ForecastIO.V2.Types import ForecastIO.V2.URI import Network (withSocketsDo) import Network.HTTP import Network.HTTP.Conduit import Network.URI downloadForecastIO :: Endpoint e => e -> IO (Either String Forecast) downloadForecastIO prov = do let uri = unpack $ buildUri prov result <- httpsConduit uri case result of Nothing -> return $ Left $ "Failed to download from: " ++ uri Just r -> do let r' = LB.filter (\x -> not $ any (== x) [ '\176' ]) r -- filter out Unicode degree symbol let ef = eitherDecode r' :: Either String Forecast case ef of Left err -> return $ Left $ "Failed to parse: " ++ err Right d -> return $ Right d httpsConduit :: String -> IO (Maybe LB.ByteString) httpsConduit uri = withSocketsDo $ do request' <- parseUrl uri let request = request' { secure = True, checkStatus = \_ _ _ -> Nothing } res <- withManager $ httpLbs request let x = responseBody res return $ if x == LB.empty then Nothing else Just x
This is pretty basic; it just takes an Endpoint instance from the forecast-io
library and downloads the result using conduit
‘s httpLbs
function. Also, as mentioned in the haddocks, I’ve noticed some aeson
parsing problems related to certain Unicode characters; my example above filters out degree symbols, which are often returned by Forecast.io JSON.
The library itself is very basic, but given that I couldn’t find a Haskell library out in the wild, I thought it would be worth open-sourcing and uploading to Hackage. It’s also my first library upload, so this is a bit of new territory for me. In any case, I hope other programmers find it handy.
One comment