Reputation: 364
given that I have a data type of:
data Number = Float Float | Rational Integer Integer
why wouldn't this code works?
createComparator :: (a -> a -> Bool) -> Number -> Number -> Bool
createComparator comparator (Float f1) (Float f2) = comparator f1 f2
createComparator comparator (Float f) (Rational n d) = comparator f $ (fromIntegral n) / (fromIntegral d)
createComparator comparator (Rational n d) (Float f) = comparator ((fromIntegral n) / (fromIntegral d)) f
-- createComparator comparator (Rational n1 d1) (Rational n2 d2) = ... -- TODO
so, it basically take a comparator (>) or (<), and 2 Number, and return Bool.
e.g: 0.5 > 1/3 == True
or, 0.2 > 1/3 == False
but, I get this error.
Couldn't match expected type ‘a’ with actual type ‘Float’
‘a’ is a rigid type variable bound by
the type signature for
createComparator :: (a -> a -> Bool) -> Number -> Number -> Bool
at Ramadoka/Parser/Number.hs:55:23
Relevant bindings include
comparator :: a -> a -> Bool
(bound at Ramadoka/Parser/Number.hs:56:20)
createComparator :: (a -> a -> Bool) -> Number -> Number -> Bool
(bound at Ramadoka/Parser/Number.hs:56:3)
In the first argument of ‘comparator’, namely ‘f1’
In the expression: comparator f1 f2
I could make it works by doing:
(|>|) :: Number -> Number -> Bool
(Float f1) |>| (Float f2) = f1 > f2
(Float f) |>| (Rational n d) = f > (fromIntegral n) / (fromIntegral d)
(Rational n d) |>| (Float f) = (fromIntegral n) / (fromIntegral d) > f
r1@(Rational _ _) |>| r2@(Rational _ _) = rationalCompare r1 r2 == GT
(|<|) :: Number -> Number -> Bool
(Float f1) |<| (Float f2) = f1 < f2
(Float f) |<| (Rational n d) = f < (fromIntegral n) / (fromIntegral d)
(Rational n d) |<| (Float f) = (fromIntegral n) / (fromIntegral d) < f
r1@(Rational _ _) |<| r2@(Rational _ _) = rationalCompare r1 r2 == LT
though, I'd still prefer to have:
(|>|) = createComparator (>)
(|<|) = createComparator (<)
Upvotes: 2
Views: 382
Reputation: 153102
Turn on Rank2Types
and write the type of the function this way:
createComparator :: (forall a. Ord a => a -> a -> Bool) -> Number -> Number -> Bool
As you wrote it, there is a particular type a
for which you are passing in a comparator; whereas you want to pass in a function which is polymorphically a comparator for many types (hence must mention forall
in its type).
Upvotes: 2
Reputation: 3739
Caveat: I do not have a Haskell compiler handy, so I can't check my work
The short answer is to just change the a
to Float
.
Let's play along though and see what happens. What your type signature is telling me is if I provide a function a -> a -> Bool
for any possible a
, your function will work. Even if I give you some ridiculous function such as String -> String -> Bool
your function will do its job. Note that for any polymorphic a
there is an implicit forall
that Haskell hides on the left of your type signature.
"For any a
you choose, you can give me an a -> a -> Bool
and I'll give you a Number -> Number -> Bool
."
forall a. (a -> a -> Bool) -> Number -> Number -> Bool
That's not what you want to say though presumably. You want your comparator to be of type Float -> Float -> Bool
since you're converting your own personal Rational
into a Float
.
If you want to figure out how to get the polymorphism you originally desired, what you're looking for is higher rank types.
So before we start this journey, enable rank-n types with this at the top of your file.
{-# LANGUAGE RankNTypes #-}
You probably want to say something closer to the following, which involves moving the forall
into a set of parentheses.
"If you give me a function such that for any chosen type a
, I can compare the two and give you a Bool
, then I can give you a Number -> Number -> Bool
."
(forall a. a -> a -> Bool) -> Number -> Number -> Bool
Moving your forall
into one layer of parentheses is what is known as a rank-2 type. If you nest another layer you get rank-3 and so on.
This still isn't what you want because the type constraint is too powerful. forall a. a -> a -> Bool
must be constantly true or false to work for all possible a
. What you probably want is a typeclass constraint to narrow down the a
you care about.
(forall a. Ord a => a -> a -> Bool) -> Number -> Number -> Bool
In your particular case, even this is basically overkill and you could just go with Float
as mentioned earlier, because that's ultimately what forall a. Ord a => a -> a -> Bool
will specialize to.
And if you have that as your signature, I think you're done (don't have a Haskell compiler handy right now).
Upvotes: 3