How does Integral work?

Type fromIntegral - (Num b, Integral a) => a -> b . I would like to understand how this is possible, what is code that can convert any integer to any type of number 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 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 Integer to Int , I don’t see how any of them generate results that can be, say, added to Float . How do they go from Int or Integer to something else?

fromInteger will be embedded in the expression, which requires it to create a specific type. He can't know what the type will be? So what is going on?

Thanks.

+5
source share
2 answers

Since fromInteger is part of the Num class, each instance will have its own implementation. None of the two implementations (for Int and Integer ) knows how to make a Float , but they are not called when you use fromInteger (or fromIntegral ) to make a Float ; what for Float instance Num for.

And so on for all other types. There is no place that knows how to turn integers into any type of Num ; this would not be possible, since it would have to support custom Num instances that do not yet exist. Instead, when each individual type is declared an instance of Num , a way should be provided to do this for that particular type (by implementing fromInteger ).

fromInteger will be embedded in an expression that requires it to produce a specific type. He can't know what the type will be? So what is going on?

In fact, knowing which type, which was expected to return from the expression in which the call is embedded, works the same way.

Type checking / output in Haskell works in two “directions” at once. It goes from top to bottom, figuring out what types each expression must have in order to fit into the larger expression in which it is used. And he also goes “from bottom to top”, figuring out what type each expression from the smaller submarine — the expression from which it is built, should have. When he finds a place where they do not match, you get a type error (exactly where the "expected type" and "actual type" you see in the error type codes).

But since the compiler has this upper knowledge ("expected type") for each expression, it is well aware that the fromInteger call fromInteger used where the Float is expected, and therefore use the Float instance for Num in this call.

+9
source

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


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

+2
source

Source: https://habr.com/ru/post/1260165/


All Articles