This small program works well with concrete types of Int
but I wanted to try to make the function the most general with type constraints.
The problem is that I don't understand the error message as the two "rigid types" seem to have the same constraints, but can't be matched.
module PotentiationNew where
import Data.Bits (FiniteBits, testBit, finiteBitSize, countLeadingZeros)
potentiation :: (Num a, Integral a, FiniteBits b) => a -> b -> a -> a
potentiation base exponent modulus =
potentiationGo $ getBits exponent
where
potentiationGo :: (Num c, Integral c) => [Bool] -> c
potentiationGo [] = 1
potentiationGo (currentExp : restExp)
| currentExp = (x * x * base) `mod` modulus -- This is the line with the error
| otherwise = (x * x) `mod` modulus
where x = potentiationGo restExp
-- Gets a bit array represented as [Bool] (True is 1)
-- Bit order starts with the least significant bits.
getBits :: FiniteBits a => a -> [Bool]
getBits x = testBit x <$> indexed
where
indexed = [0 .. finiteBitSize x - countLeadingZeros x - 1]
Here is the error message:
Potentiation.hs:13:22: error:
• Couldn't match expected type ‘c’ with actual type ‘a’
‘a’ is a rigid type variable bound by
the type signature for:
potentiation :: forall a b.
(Num a, Integral a, FiniteBits b) =>
a -> b -> a -> a
at Potentiation.hs:6:17
‘c’ is a rigid type variable bound by
the type signature for:
potentiationGo :: forall c. (Num c, Integral c) => [Bool] -> c
at Potentiation.hs:10:23
Just because two types have the same constraints doesn't mean they're the same type. For instance,
Int
isNum
, andInteger
isNum
, butInt
is not the same asInteger
.In
potentiationGo
, you use the valuesmodulus
andbase
, both of which are of typea
, in positions where your type signature says there should be a value of typec
. These two types aren't the same!The easiest way to fix your problem is to remove the type signature for
potentiationGo
, which should remove the error but still keep things general (GHC is pretty good about automatically deriving the most general types).That said, if you're like me, you really like having the type signature explicit because, at the very least, it acts as a good sanity check and makes your code a little more self-documenting. In this case, what you really want to write is that
potentiationGo :: [Bool] -> a
, wherea
is the samea
as in your type signature forpotentiation
itself. If you try this, it won't work, because GHC defaults to creating new type variables for every signature. To counteract this, you need to enableScopedTypeVariables
, which will let GHC know that the twoa
s are the same. This will also mean you need to introduce the typea
with theforall
keyword (and once you're usingforall
, you need to introduce every type variable in the type). It'll look something like this:This is as general as you can get.
Note that this doesn't affect your
getBits
function becausegetBits
isn't directly using the valueexponent
. Rather, that value is being passed as an argument. By this logic, you can also fixpotentiationGo
by writing something like:Notice how the values of type
a
are being provided as arguments topotentiationGo
now. Within potentiationGo, the values are of typec
. The typesc
anda
are never unified as equal, but becausea
isNum
andIntegral
, it can be passed topotentiationGo
as ac
.