2

An expression like 2^(2%1) does not typecheck in GHCi, and the error messages are cryptic. Why does this not work, what do I need to change?

I can't convert to another type, I want this for expressions like 27^(1%3).

Bakuriu
  • 98,325
  • 22
  • 197
  • 231
Ludwik
  • 2,247
  • 2
  • 27
  • 45
  • 6
    What type do you want the result to be? – Lambda Fairy Aug 24 '15 at 06:15
  • Related: http://stackoverflow.com/q/6400568/2541573 – jub0bs Aug 24 '15 at 06:16
  • Well I'd expect the result of the first one to be 4, the second one to be 3. – Ludwik Aug 24 '15 at 06:29
  • And @Jubobs: Yeah, and the other two don't typecheck either. I thought it would work, since `Data.Ratio` is an instance of `Integral`, so it seems to fit with the type signature of `(^)`. – Ludwik Aug 24 '15 at 06:31
  • 2
    @Ludwik There is a `Integral a => Fractional (Ratio a)` instance, but there is no `Integral` instance for `Ratio a`. – jub0bs Aug 24 '15 at 06:44
  • 7
    AFAIK there are three power operators in Haskell: `(^) :: (Num a, Integral b) => a -> b -> a`, `(^^) :: (Fractional a, Integral b) => a -> b -> a` and `(**) :: (Floating a) => a -> a -> a`. So the exponent is either `Integral` or `Floating` but there is no `Fractional` exponent available... you should use `27**(realToFrac $ 2%3)`. – Bakuriu Aug 24 '15 at 07:00
  • 6
    I second Lambda Fairy; what *type* do you want the result to be? Do you want a `Ratio`? (2^(1/2) isn't rational.) Do you want the nearest whole number? Do you want a `Double`? What *type* do you want? – MathematicalOrchid Aug 24 '15 at 11:49
  • @MathematicalOrchid Thanks, that cleared my mind. I see the reason it didn't work now. – Ludwik Aug 26 '15 at 18:16

2 Answers2

6

Haskell has three power operators:

  1. (^) :: (Num a, Integral b) => a -> b -> a

    This raises any type of number using a positive integral exponent.

    You are getting an errors like Could not deduce (Integral (Ratio a0)) arising from a use of ‘^’ when typing 2^(2%3) because Data.Ratio is not an instance of Integral. GHC sees that ^ wants an Integral and notices Data.Ratio cannot be used in such a situation.

  2. (^^) :: (Fractional a, Integral b) => a -> b -> a

    This operator allows negative integral exponents. Remember that x^(-n) == 1/(x^n). That's why it requires Fractional.

    Note that the exponent has to still be integral. 2^^(1%2) would not be a Fractional number.

  3. (**) :: Floating a => a -> a -> a

    This is the "catch all" operator. It can raise a fractional number to a fractional power. However this uses floating point numbers, not exact rationals.

Since we cannot represent all real numbers they have decided to simply rely on floats when you want inexact operations.

So you should use type conversions to perform that operation. A possible implementation could be:

realToFrac $ 27**(realToFrac $ 2%3) :: Rational

Or you may define a new operator:

(*^*) :: (RealFrac a, RealFrac b) => a -> b -> a
x *^* y = realToFrac $ realToFrac x ** realToFrac y

Which would allow you to write:

27 *^* (2%3)

I used two * to remind of the ** which is used in the implementation and I've added a ^ to refer to the type of the first two operators... not sure whether that makes sense, or maybe ^** or ^^* would have been better.

However it may be better to simply use Doubles. It really depends on what the number represents and what you are doing with them.

Bakuriu
  • 98,325
  • 22
  • 197
  • 231
5

To adress “why does this not work” – Haskell's multiple exponentiation operators actually reflect quite closely the way mathematicians define exponentiations!

  • Ab initio, you only know (directly by definition) that for natural n, the expression xn denotes 1·x·x·x…, with n uses of x. That's exactly what ^ implements, and it obviously works for any number type at all (because multiplication is always defined).
  • By “reverse continuation” across zero it makes sense to define x-n as 1/x·x·x. This only works when you have multiplicative inverses, i.e. fractions. Well, Fractional, you know! This is what ^^ does.
  • To define fractional exponents, you can start with considering roots. Those are essentially what you're asking for. However, roots are in fact mathematically quite involved. They aren't really unique. The result type for such roots would be something representing the algebraic numbers. For most users, there's not much of a reason to have a dedicated treatment for those numbers, hence Haskell skips this.
    Now you say, well, but we could certainly define roots for particular numbers. The third root of 27 happens to be an integer, but for most other numbers this is not true. Ill-specified or partial functions are evil; we would therefore need a signature like Num a => a -> Rational -> Maybe a. But the result would usually be Nothing, so this would be a pretty useless function honestly. Implement it yourself if you like.
  • It's more useful if you straightly go one step further: switching from algebra to calculus, the mathematician gets access to the exponential and logarithm functions, and these give you a cover-all for all power expressions, by virtue of the equality
                xy = (exp(ln x))y = exp(y · ln x).
    This works whenever x is positive. For the definition of exponentials, mathematically you need limits. These exist in complete spaces, i.e. “spaces without holes”. The real numbers are complete by definition, but the rationals are not! To handwave the reason: Rational is “infinitely accurate”, i.e. each value is really a single exact number. However, you can in reality only ever use a finite number of such rationals.
    OTOH, floating-point types are by design not exact. This often leads to confusion, but mathematically it can actually be interpreted quite nicely: every Double value represents a whole interval of real numbers, and if you take the unit of all those values you get not just a set of discrete points but the whole real line.
    And that's how the Floating class with its instance for Double represents complete spaces, and why this class has the method (**) :: Floating a => a -> a -> a.

In the complex numbers you can even define the logarithm of negative numbers, though it leads again to uniqueness problems.

leftaroundabout
  • 117,950
  • 5
  • 174
  • 319