Can Haskell type synonyms be used as type constructors?

362 views Asked by At

I'm writing a benchmark to compare the performance of a number of Haskell collections, including STArray, on a given task. To eliminate repetition, I'm trying to write a set of functions that provide a uniform interface to these collections, so that I can implement the task as a polymorphic higher-order function. More specifically, the task is implemented in terms of a polymorphic monad, which is ST s for STArray, and Identity for collections like HashMap, that do not typically need to be manipulated within a monad.

Due to uniformity requirements, I can't use the Identity and HashMap types directly, as I need their kinds to match the kinds of ST and STArray. I thought that the simplest way to achieve this would be to define type synonyms with phantom parameters:

type Identity' s a = Identity a
type HashMap' s i e = HashMap i e
-- etc.

Unfortunately this doesn't work, because when I try to use these synonyms as type constructors in places where I use ST and STArray as type constructors, GHC gives errors like:

The type synonym ‘Identity'’ should have 2 arguments, but has been given none

I came across the -XLiberalTypeSynonyms GHC extension, and thought it would allow me to do this, as the documentation says:

You can apply a type synonym to a partially applied type synonym

and gives this example of doing so:

type Generic i o = forall x. i x -> o x
type Id x = x

foo :: Generic Id []

That example works in GHC 8.0.2 (with -XExistentialQuantification and -XRank2Types). But replacing Generic with a newtype or data declaration, as needed in my use case, does not work. I.e. the following code leads to the same kind of error that I reported above:

newtype Generic i o = Generic (forall x. i x -> o x)
type Id x = x
foo :: Generic Id []
foo = Generic (\x -> [x])

Question

Is there some other extension that I need to enable to get this to work? If not, is there a good reason why this doesn't work, or is it just an oversight?

Workaround

I'm aware that I can work around this by defining Identity', etc. as fully-fledged types, e.g.:

newtype Identity' s a = Identity' a
newtype Collection collection s i e = Collection (collection i e)
-- etc.

This is not ideal though, as it means that I have to reimplement Identity's Functor, Applicative and Monad instances for Identity', and it means that I have to write additional wrapping and unwrapping code for the collections.

0

There are 0 answers