2

I am working on a webapp using Happstack and I am writing some code to store my types in MongoDB. I felt like shortening my code some by putting the code into a typeclass so that I could use the same code to read and write to the database for different types. Something like this:

class DatabaseType a where
    toDoc           :: a -> Document
    fromDoc         :: Document -> a
    saveCollection  :: Text
    getFromDatabase :: (MonadIO m) => Pipe -> Text -> Value -> m a
    getFromDatabase pipe field value = ...
    ...

Now the problem here is the saveCollection, since it doesn't use any of the type variables GHC won't let it compile, however it is very important to the database functions (like getFromDatabase) so that they know which collection to save to.

Question is, how to have a value in a typeclass that is not bonded by the type variables.

1 Answer 1

5

You have to add the type variable. The easiest way is to use a proxy:

  saveCollection :: proxy a -> Text
  -- Note the `proxy` is lower case

instance DatabaseType MyDB where
  saveCollection _ = "MyDB"

Now to use it, you'll likely do this:

import Data.Proxy

foo = saveCollection (Proxy :: Proxy MyDB)

The reason for using lower case in the method declaration is convenience: you can use any value whose type has the right form instead of Proxy MyDB, if you happen to have one on hand at the call site.


There are some situations where the standard proxy technique can lead to a problematic loss of sharing. This happens because results of function calls are not generally memoized. In this case, you can used a tagged type. Data.Tagged defines

newtype Tagged s b = Tagged {unTagged :: b}

Tagged types are much more awkward to work with than proxies, unless you use partial type signatures or explicit type application, two of GHC's most recently added features. If you wanted, though, you could write

saveCollection :: Tagged a Text

Then in the instance,

saveCollection = Tagged "Hi there."

Using it directly would require something like

unTagged (saveCollection :: Tagged MyDB Text)

or, with partial type signatures,

unTagged (saveCollection :: Tagged MyDB _)

or with explicit type application I think something like

unTagged (saveCollection@MyDB)

Thanks to this awkwardness, the tagged package offers functions for converting between proxy-based and tagged representations.

Sign up to request clarification or add additional context in comments.

2 Comments

This is indeed the idiomatic solution. (+1) Still, I have never fully bought the use of proxy a, even if this has become very popular. I would consider passing e.g. [a] instead of Proxy a to be obfuscating: if the intended value is irrelevant, and only the type matters, I'd rather pass an explicit Proxy a. Hopefully, GHC 8 and explicit type args will wipe proxies away.
@chi, I usually agree. However, it can be very convenient to be able to use a singleton as a proxy. If I have assoc :: Natty x -> Natty y -> proxy z -> (x :+ y) :+ z :~: x :+ (y :+ z) and I have singletons representing three natural numbers, I can just pass them in without first converting the third to a proxy.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.