I just want to clear my doubts on the fromIntegral type in Haskell.
The output for these 2 euclidean distance functions are the same, so what's the point of putting fromIntegral? Like they both give floating point values of the euclidean distances.
Also, for the type definition of my function distance2 which uses the fromIntegral type, why is it (Floating a1, Integral a2) and then => (a2, a2) -> (a2, a2) -> a1? I just don't quite get the interpretation of it here.
distance2 :: (Floating a1, Integral a2) => (a2, a2) -> (a2, a2) -> a1
distance2 (x1, y1) (x2, y2)
= sqrt (fromIntegral ((x2-x1)^2 (y2 - y1)^2))
distance3 :: Floating a => (a, a) -> (a, a) -> a
distance3 (x1, y1) (x2, y2)
= sqrt ((x2-x1)^2 (y2 - y1)^2)
Could someone please help with an explanation, thank you :)
CodePudding user response:
The output for these 2 euclidean distance functions are the same, so what's the point of putting fromIntegral? Like they both give floating point values of the euclidean distances.
They both give floating point values, but they don't both take floating point values.
> distance3 (pi, 0) (0, 0)
3.141592653589793
> distance2 (pi, 0) (0, 0)
<interactive>:2:1: error:
• Could not deduce (Integral a20) arising from a use of ‘distance2’
from the context: Floating a1
bound by the inferred type of it :: Floating a1 => a1
<snipped considerable additional error text>
Also, for the type definition of my function distance2 which uses the fromIntegral type, why is it (Floating a1, Integral a2) and then => (a2, a2) -> (a2, a2) -> a1?
Your distance2
function takes numbers, and returns numbers, but not the same type of numbers. The incoming numbers must be integer-like things, so that you can apply fromIntegral
to them; but the outgoing numbers must be floating-point-like things, so that you can apply sqrt
to them. For example, here are some monomorphic types that distance2
can be specialized to:
(Int, Int) -> (Int, Int) -> Double
(Integer, Integer) -> (Integer, Integer) -> Double
(Word, Word) -> (Word, Word) -> Float
This English description is captured by creating two type variables, each with different constraints. An identical type for distance2
, but with somewhat more human-readable variable names, might look like this:
(Floating float, Integral int) => (int, int) -> (int, int) -> float
CodePudding user response:
fromIntegral
is used to convert the value of type Integral a => a
that your sum-of-squares produces into a value of type Floating a => a
that sqrt
expects.
fromIntegral
has type (Integral a, Num b) => a -> b
. That means that, given a value of type Integral a
, it will give you back a value of any type b
that has a Num
instance. Since Floating
has Num
as a superclass (by way of Fractional
), that means fromIntegral
can produce a value of type Floating a => a
.
CodePudding user response:
There are two relevant typeclasses here:
Integral
is the typeclass for types representing a subset of integers- each
Integral
type has a functiontoInteger
that converts its values to the arbitrary-length integer typeInteger
- each
Num
is the typeclass for numeric types that include integers as a subset- each
Num
type has a functionfromInteger
that converts arbitrary-lengthInteger
values to values of thatNum
type
- each
The prelude function fromIntegral
is defined as:
fromIntegral = fromInteger . toInteger
It uses both of those functions to convert any Integral
type to any Num
type.
This is what lets your first function take any Integral
type as input: the input type is inferred through the sum-of-squared-differences expression as the input to fromIntegral
, and selects toInteger
from that type's instance of Integral
, while the Floating
output type is inferred through the sqrt
function to demand it as a result from the fromIntegral
call, and selects the fromInteger
from the output type's Num
instance.