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,
IntisNum, andIntegerisNum, butIntis not the same asInteger.In
potentiationGo, you use the valuesmodulusandbase, 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, whereais the sameaas in your type signature forpotentiationitself. 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 twoas are the same. This will also mean you need to introduce the typeawith theforallkeyword (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
getBitsfunction becausegetBitsisn't directly using the valueexponent. Rather, that value is being passed as an argument. By this logic, you can also fixpotentiationGoby writing something like:Notice how the values of type
aare being provided as arguments topotentiationGonow. Within potentiationGo, the values are of typec. The typescandaare never unified as equal, but becauseaisNumandIntegral, it can be passed topotentiationGoas ac.