Search code examples
haskelltypesapi-designparametric-polymorphism

Can I constrain the parametric polymorphic type on type/data constructor in Haskell?


I have a parameterised type that I'd like to constrain to a numeric type, more specifically a Fractional, e.g.:

data Rating a = (Fractional a) => Score a | Unscored deriving (Show, Eq)

so that the user of the API can define which non-integer type they might utilise (Float or Double?), but the internal API code I write can still perform arithmetic operations on a numeric type. I do not want it to be an integer because results of my "internal operations" may not be integers and my understanding is that using Fractional would lead to more accurate results.

Compiling the above (in GHCI at least) gives me the following error:

    Data constructor `Score' has existential type variables, a context, or a specialised result type
  Score :: forall a. Fractional a => a -> Rating a
  (Use ExistentialQuantification or GADTs to allow this)
In the definition of data constructor `Score'
In the data declaration for `Rating'

which suggests to me I'm doing something that I probably don't want to keep trying; i.e. my design is rubbish.

I guess I'm trying to say the following in this API: "when you use a Rating type, its parameter must be a subclass of Fractional so I can perform accurate arithmetic on it". How might I achieve this? Or am I way off the mark and/or overengineering?


Solution

  • You shouldn't put the Fractional constraint on the datatype, but rather on the functions that work with it. So

    data Rating a = Score a | Unscored deriving (Show, Eq)
    
    makeSomeRating :: Fractional a => a -> Rating a
    makeSomeRating x = Score (x / 2) -- can use Fractional functions here
    
    doSomethingElseWithRating :: Fractional a => Rating a -> Something