Revising the BitX haskell bindings

Context: my previous blog post announcing the BitX bindings.

So a week ago I released the first version of the bitx-bitcoin library and lo and behold! I am now releasing a major revision. I decided that it would probably be a good idea to document what has changed so that the dozen-or-so loyal users of my library (most of them are probably mirrors, but whatever) can get some context before they inundate me with complaints about breaking changes impacting their high-availability financial systems.

More importantly, I thought I would like to briefly share all the shiny new improvements introduced in the code, primarily as a result of discussions on this Reddit thread. The purpose here is to learn together, so let’s start.

Tips and tricks

Most of the changes are refactorings with zero impact on functionality. These are basically either idioms I knew about but overlooked due to lack of experience, or brand knew tricks I was not aware of.

Scientific instead of Decimal

One of the major design decisions in the first incarnation of the library was figuring out a numeric data type to use to avoid errors due to rounding in financial values. As a general principle, one should never use floating point for financial arithmetic. I looked around and decided to use the relatively-not-popular Decimal package. I think my Google-fu failed me in this instance.

It turns out that there is a more popular alternative with essentially the same functionality, with the added benefit that it is a dependency of aeson: the scientific package. Due to the magic of typeclasses, the change is simply searching for the word Decimal and replacing it with Scientific. Fewer transitive dependencies is always a good thing, methinks.

Incidentally, this was the first of two API-breaking changes, and thus warranted bumping up the major version number of the package.

Fun with Applicative (and friends)!

Oooohh. The A-word! I was shown an oportunity to use a very common Haskell idiom in the core network code. Basically, the idiom goes something like this:

If you want to try various alternatives, each one using the Maybe monad to signify success or failure, with a final default option should all others fail, then use the Applicative (and Alternative) instances of Maybe.

In particular, this

let a = doSomeStuff
case a of
  Just α  -> return . prepareAnswerA $ α
  Nothing -> do
    let b = doSomeOtherStuff
    case b of
      Just β  -> return . prepareAnswerB $ β
      Nothing -> return defaultAnswer

can be rewritten as this

fromJust $
      prepareAnswerA <$> doSomeStuff
  <|> prepareAnswerB <$> doSomeOtherStuff
  <|> Just defaultAnswer

To understand this a bit better, we need to look at the Applicative instance of Maybe:

instance Applicative Maybe where
  pure = Just

  Just f  <*> m  = fmap f m
  Nothing <*> _m = Nothing

<$> is an alias for fmap, in that f <$> m = fmap f m. fmap comes from the Functor instance:

instance Functor Maybe where
  fmap _ Nothing  = Nothing
  fmap f (Just a) = Just (f a)

which seems like an obvious definition given that Functor is simply the typeclass of things which provide a mapping operation. Putting everything together, we basically have the following:

g <$> Just a  = Just . g $ a
g <$> Nothing = Nothing

So, that seems simple enough. Take a pure function g, and return a Maybe which is either g applied to the contents of a Just, or Nothing. The Alternative instance of Maybe looks like this:

instance Alternative Maybe where
  empty = Nothing

  Nothing <|> r = r
     l    <|> _ = l

Don’t worry about empty. Rather, note that if the left of <|> is Nothing then it will try the right, otherwise it will return the left. That is, a chain of <|>s will try a sequence of Maybes until it finds the first Just, and it will return the first Just (<|> is left associative) or the final element (which might be Nothing if there are no Justs in the sequence).

After we have tried all the alternatives, and given a default response, we then use fromJust to convert the entire thing into a pure value. Note that since in general we might potentially not find a single Just, our program could crash at run time since fromJust is a partial function. This is not an issue in our use of the idiom since we give a default value as the final option.

For completeness, the code in question in the BitX bindings now looks like this:

bitXErrorOrPayload 
  :: BitXAesRecordConvert rec aes 
  => Response BL.ByteString -> BitXAPIResponse rec
bitXErrorOrPayload resp = fromJust $
      ErrorResponse . aesToRec <$> Aeson.decode body
  <|> ValidResponse . aesToRec <$> Aeson.decode body
  <|> Just (UnparseableResponse  resp)
  where
      body = NetCon.responseBody resp

Note the apparently repeated code on lines 5 and 6. This is due to the FunctionalDependencies magic I explained in the previous blog post (Haskell will infer the correct — and different — types automatically).

RecordWildcards

This one was an idiom I did not recognise, although I had read a little about the extension in question. Essentially, the RecordWildcards extensions enables you to use — under certain contexts — record field accessor functions as if they were field names. Recall the following snippet from the previous blog post:

data Ticker_ = Ticker_
  { ticker'timestamp :: TimestampMS
  , ticker'bid :: QuotedDecimal
  , ticker'ask :: QuotedDecimal
  , ticker'last_trade :: QuotedDecimal
  , ticker'rolling_24_hour_volume :: QuotedDecimal
  , ticker'pair :: CcyPair
  }

$(AesTH.deriveFromJSON AesTH.defaultOptions{AesTH.fieldLabelModifier = last . splitOn "'"}
''Ticker_)

instance BitXAesRecordConvert Ticker Ticker_ where
  aesToRec (Ticker_ ticker''timestamp ticker''bid ticker''ask ticker''lastTrade
    ticker''rolling24HourVolume ticker''pair) =
    [record| {timestamp = tsmsToUTCTime ticker''timestamp,
      bid = qdToDecimal ticker''bid,
      ask = qdToDecimal ticker''ask,
      lastTrade = qdToDecimal ticker''lastTrade,
      rolling24HourVolume = qdToDecimal ticker''rolling24HourVolume,
      pair = ticker''pair} |]

Note, in particular, that, not only do we need to add prefixes to the record field names to get around the records’ problem in Haskell, we also do a little pattern matching in the instance declaration. I thought I had two choices when naming the fields to pattern match on:

  1. Use the same names as in the record declaration, in which case the Haskell compiler will complain about the names shadowing the functions defined in the record declaration (because in Haskell these are accessor functions in global scope, they are not really field names): aesToRec (Ticker_ ticker'timestamp ...) ... timestamp = ticker'timestamp, or
  2. Make the names I use to pattern match different from the field names, in which case the Haskell compiler complains about me not using the “field names” I created in the record declaration (remember, the only real reason I had to name these fields was so that Data.Aeson.TH could do its magic): aesToRec (Ticker_ ticker''timestamp ...) ... timestamp = ticker''timestamp.

Of course, there was a third alternative I didn’t consider at the time — that of actually using the “field” names in the record declaration for what they are, accessor functions: aesToRec tic ... timestamp = ticker'timestamp tic. But the RecordWildCard extension gives us something much better than all three alternatives: we get to pretend, if only for a little bit, that the record “fields” are actually field names rather than accessors.

instance BitXAesRecordConvert Ticker Ticker_ where
  aesToRec (Ticker_ {...}) =
    [record| {timestamp = tsmsToUTCTime ticker'timestamp,
      bid = qdToDecimal ticker'bid,
      ask = qdToDecimal ticker'ask,
      lastTrade = qdToDecimal ticker'lastTrade,
      rolling24HourVolume = qdToDecimal ticker'rolling24HourVolume,
      pair = ticker'pair} |]

This is not an earth-shattering refactor, but it’s nice to use when you can find an opportunity. As a side-effect, I no longer had to suppress the Unused bindings warnings the compiler was producing due to the way I had done this initially.

Other changes

Other changes were more specific to this package rather than Haskell idioms.

Removing the Auth module

I had to remove Network.Bitcoin.BitX.Private.Auth. It never felt quite right including it in the first place (it sounds more like something that should be done manually, as with the rest of the steps in the OAuth2 section of the BitX API docs), and the API’s behaviour didn’t quite match the rest of the API, and I never got around to testing it properly (by asking for a developer key). I have communicated with the BitX developers, and apparently a revision of the Auth endpoint is in the works, so I shall consider re-introducing the bindings once that work has been done.

Creating smaller sub-modules

I divided the main Network.Bitcoin.BitX.Private module into smaller sub-modules. Nothing to see here…

Adding a new Create Account function

The BitX developers created a new API endpoint for creating an account. I had to wait a day or two before releasing the library while they fixed an issue I discovered during testing, as well as another, milder, issue with the endpoints for withdrawals.

In conclusion

Apparently, announcing the release of this library on Reddit was A Good Idea™. It’s amazing how much one can improve their code and understanding once they ask others to take a look at their work.

Incidentally, reports about this legendary helpfulness and friendliness of the Haskell community was one of the main reasons which led me to start learning Haskell over LISP a few years ago, after I had made up my mind to find out what the fuss over functional programming was all about.

On a completely unrelated note, coding against the BitX API is starting to feel like aiming at a moving target. So much so, in fact, that for a moment I considered abandoning the principles of the semantic versioning system and using minor versions for API changes. It looks strange to release a major revision after only a week, and I have a feeling that this won’t be the last time a change in the BitX endpoints will prompt me to review my library interface again. This fact, coupled with how developers typically refer to library version numbers in their Cabal files, means that a revision of this library could potentially become “bitrotten” in a matter of a few weeks. So maybe you should just not have upper bounds — and use qualified imports — when using this library…

Oh, by the way, I used hscolour to generate the highlighting for the code snippets in this post (because free WordPress does not support Haskell code highlighting, and F# turns out to be a terrible proxy as evidenced by my previous blog post). Next Haskell post I will try to use BlogLiterately.

Advertisements
This entry was posted in Haskell, Programming and tagged , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s