Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Haskell, is there a good way to write a num to num conversion function `toNum :: (Num a, Num b) => a -> b`?

For example, one bad way is to factor through a string:

toReadableNum :: (Num a, Num b, Read b) => a -> b
toReadableNum = read . show

If there are no good ways, are there other bad ways? Implementation specific? Requiring language extension?

like image 777
wl. Avatar asked Jul 23 '11 06:07

wl.


3 Answers

You can't go (sanely) from Num to Num, as Num provides no mechanism for extracting information about the value held other than its spurious Eq and Show machinery, but if you are willing to assume a bit more on the behalf of the number you are coming from, then you can have recourse.

In particular

fromIntegral :: (Integral a, Num b) => a -> b

and the composition of

toRational :: Real a => a -> Rational

with

fromRational :: Fractional a => Rational -> a

are both good candidates for doing what you mean, if not exactly what you asked for.

While read . show is well typed and has the signature you propose, the meaning is gobbledigook. There is nothing at all that says the text emitted by one Show instance will be compatible with a completely different Read instance, and there are plenty of counter examples.

The (implied) contract on Read and Show only apply when you use them with the same type!

like image 183
Edward Kmett Avatar answered Nov 05 '22 09:11

Edward Kmett


There are no good ways. Some numbers contain more information that other, so how could you expect to convert between two arbitrary numbers in a good way. Some simple examples: How do you convert a Double to an Int? A Rational to on Int8? A Complex Double to a Float?

All this involve information loss and then there is no obvious right way.


And as @hammar says, the operations in Num simply don't allow you to construct such a function.

like image 33
augustss Avatar answered Nov 05 '22 07:11

augustss


You cannot write any useful function of the type (Num a, Num b) => a -> b. Since a and b are type variables, the only useful operations we can use on them are the ones in the Num class. (Eq and Show won't help us much here).

class  (Eq a, Show a) => Num a  where
    (+), (-), (*)       :: a -> a -> a
    negate              :: a -> a
    abs                 :: a -> a
    signum              :: a -> a
    fromInteger         :: Integer -> a

The only function here that allows you to make an b if you didn't have one to start with is fromInteger, but you have no way of turning a into an Integer, so the only functions you can write of this type return fromInteger of some constant, or bottom. Not very useful.


As augustss pointed out, there is no obvious way of making this conversion anyway. Remember lots of types can be Num. Not only the various types of real numbers, but also complex numbers, matrices, polynomials, etc. There is no meaningful conversion that would work between all of them.

like image 39
hammar Avatar answered Nov 05 '22 08:11

hammar