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?
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