stusmith
stusmith

Reputation: 14093

How do I specify types for a function, where they are not used in the function's arguments?

I'm writing some data-access routines, using Persistent. I want my API to be defined in terms of datatypes which represent JSON, but on the persistent side, my datatypes are defined by persistent's templating system.

Given that I have mappings from json to database datatypes, and vice-versa, I thought I should be able to write generalised data access routines.

All was going well until I tried to write the insert function:

standardInsert :: forall d . forall j .
                  (PersistEntityBackend d ~ SqlBackend, PersistEntity d, SimpleJsonDataAccessConversion j d)
               => j -> DatabaseEnvironmentT (Maybe (WithId j))
standardInsert json = do
    maybeId <- runSqlMaybe $ insert db
    return $ toApi <$> maybeId
  where db        = jsonToDataAccess json :: d -- Scoped type variable here.
        toApi key = addId key $ dataAccessToJson db

(j is the type variable for the JSON data type, d is the type variable for the persistent data type).

This function has two type variables, j and d, but only j can be inferred from the arguments.

In other words, if I call standardInsert jsonValue, the type variable d is ambiguous.

I want to call it rather like in C++ - standardInsert<FooJsonType, FooPersistentType>(jsonValue).

How do I tell Haskell what d is? Or am I going about this in completely the wrong way?

Upvotes: 6

Views: 108

Answers (1)

Ganesh Sittampalam
Ganesh Sittampalam

Reputation: 29100

GHC won't be able to infer the type variable d. You need to add it to the type signature itself, by adding a dummy argument. The standard trick is to use a proxy for this dummy argument, which means the caller doesn't need to give an actual value of that type.

You can get Proxy from the tagged package for GHC<7.8, or from base for GHC>=7.8, but for the purposes of the explanation I'll define it explicitly here:

data Proxy a = Proxy

standardInsert :: forall d . forall j .
                  (PersistEntityBackend d ~ SqlBackend,
                   PersistEntity d, SimpleJsonDataAccessConversion j d)
               => Proxy d -> j -> DatabaseEnvironmentT (Maybe (WithId j))
standardInsert _ json = do (...)

and then at a call site:

standardInsert (Proxy :: Proxy FooPersistentType) jsonValue

Upvotes: 9

Related Questions