Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does fromIntegral work?

Tags:

haskell

The type of fromIntegral is (Num b, Integral a) => a -> b. I'd like to understand how that's possible, what the code is that can convert any Integral number to any number type as needed.

The actual code for fromIntegral is listed as

fromIntegral = fromInteger . toInteger

The code for fromInteger is under instance Num Int and instance Num Integer They are respectively:

instance  Num Int  where
  ...
  fromInteger i = I# (integerToInt i)

and

instance  Num Integer  where
  ...
  fromInteger x  =  x

Assuming I# calls a C program that converts an Integer to an Int I don't see how either of these generate results that could be, say, added to a Float. How do they go from Int or Integer to something else?

fromInteger will be embedded in an expression which requires that it produce a certain type. It can't know what the required type will be? So what happens?

Thanks.

like image 747
RussAbbott Avatar asked Nov 22 '16 21:11

RussAbbott


2 Answers

Because fromInteger is part of the Num class, every instance will have its own implementation. Neither of the two implementations (for Int and Integer) knows how to make a Float, but they aren't called when you're using fromInteger (or fromIntegral) to make a Float; that's what the Float instance of Num is for.

And so on for all other types. There is no one place that knows how to turn integers into any Num type; that would be impossible, since it would have to support user-defined Num instances that don't exist yet. Instead when each individual type is declared to be an instance of Num a way of doing that for that particular type must be provided (by implementing fromInteger).

fromInteger will be embedded in an expression which requires that it produce a certain type. It can't know what the required type will be? So what happens?

Actually, knowing what type it's expected to return from the expression the call is embedded in is exactly how it works.

Type checking/inference in Haskell works in two "directions" at once. It goes top-down, figuring out what types each expression should have, in order to fit into the bigger expression it's being used in. And it also goes "bottom-up", figuring out what type each expression should have from the smaller sub-expressions it's built out of. When it finds a place where those don't match, you get a type error (that's exactly where the "expected type" and "actual type" you see in type error messages cone from).

But because the compiler has that top-down knowledge (the "expected type") for every expression, it's perfectly able to figure out that a call of fromInteger is being used where a Float is expected, and so use the Float instance for Num in that call.

like image 167
Ben Avatar answered Sep 22 '22 22:09

Ben


One aspect that distinguishes type classes from OOP interfaces is that type classes can dispatch on the result type of a method, not only on the type of its parameters. The classic example is the read :: Read a => String -> a function.


fromInteger has type fromInteger :: Num a => Integer -> a. The implementation is selected depending on the type of a. If the typechecker knows that a is a Float, the Num instance of Float will be used, not the one of Int or Integer.

like image 36
danidiaz Avatar answered Sep 22 '22 22:09

danidiaz