I'm going to use C++ as an example here to show what I'm after. For complex arithmetic it has both complex and imaginary types:
https://en.cppreference.com/w/c/language/arithmetic_types#Imaginary_floating_types
These e.g. have the property that multiplying two numbers with type double imaginary will have the type double. This is almost but not quite the same as using complex numbers with the real part being 0.0 but not quite. Imaginary types don't explicitly store the real part which automatically eliminates unneeded computations with and storage of 0.0.
Additionally it prevents some problems with signed zeros. E.g. the computation (0.0+i*a)*(0.0+i*b) results in (-a*b-i*0.0) if a and b are negative and (-a*b+i*0.0) otherwise. This can be surprising if the result is fed into a function with a branch cut. An imaginary type avoids this unwanted negation of the zero.
My question is can you define a similar imaginary type in Haskell (in addition to a complex type) and also the operations (+)
, (-)
, (*)
, and (/)
for it such that they behave like in C++? It seems that at least with the current definition of the Num
and Fractional
classes it's not possible because (+)
, (-)
, (*)
, and (/)
have a -> a -> a
as type signature so e.g. multiplying two imaginary numbers can't have a different type as a result. Could one, however, a different definition for these classes so that what I'm after would be possible?
I'm not asking this for a practical purpose. I just want to better understand what Haskell's type system is capable of.