The fromIntegral
returns a Num
data type. But it seems that Num
is able to coerce to Double
or Integer
with no issue. Similarly, the read
function is able to return whatever that is required to fit its type signature. How does this works? And if I do need to make a similar function, how do I do it?
The type checker is able to infer not only the types of function arguments but also the return type. Actually there is no special case there. If you store the result of fromIntegral
or read
in Integer
, the version for this type will get called. You can create your own function in the same way.
For example:
class Foo a where
foo :: a
instance Foo Integer where
foo = 7
instance Foo String where
foo = "Hello"
x :: Integer
x = 3
main = do
putStrLn foo
print (foo + x)
Because putStrLn
has type String -> IO ()
the type checker finds that the type of foo
in putStrLn foo
must be String
for the program to compile. The type of foo
is Foo a => a
. So it deduces that a == String
and searches for Foo String
instance. Such instance exists and it causes the foo :: Foo String => String
value to be selected there.
Similar reasoning happens in print (foo + x)
. x
is known to be Integer
, and because the type of (+)
is Num a => a -> a -> a
the type checker deduces that the left argument of the addition operator must also be Integer
so it searches for Foo Integer
instance and substitutes the Integer
variant.
There is no direction from function arguments to (possible) return type like in C++. The type checker may even deduce function arguments based on knowledge of what the function is expected to return. Another example:
twice :: a -> [a]
twice a = [a,a]
y :: [Integer]
y = twice foo
The function argument here is of type Foo a => a
which is not enough to decide which foo
to use. But because the result is already known to be [Integer]
the type checker finds that it has to provide value of type Integer
to twice
and does so by using the appropriate instance of foo
.
read
is a member of a typeclass which means that its implementation can depend on a type parameter.
Also, numeric literals like 1
, 42
, etc. are syntatic sugar for function calls fromInteger 1
, fromInteger 42
, etc., and fromInteger
itself is a member of the Num typeclass.
Thus, the literal 42
(or fromInteger 42
) can return an Int
or Double
or any other Num instance depending on the context in which it is called.
I'm being a little particular about terminology here, because I think the words you're using betray some misunderstandings about how Haskell works.
The fromIntegral returns a Num data type.
More precisely, fromIntegral
takes as input any type that has an instance of class Integral
, and can return any type that is an instance of class Num
. The prelude defines it like this:
fromIntegral :: (Integral a, Num b) => a -> b
fromIntegral = fromInteger . toInteger
Types with an Integral
instance implement the toInteger
function, and all types with a Num
instance implement fromInteger
. fromIntegral
uses the toInteger
associated with the Integral a
instance to turn a value of type a
into an Integer. Then it uses the fromInteger
from the Num b
instance to convert that Integer into a value of type b
.
But it seems that Num is able to coerce to Double or Integer with no issue.
Every time a haskell function is used, it is "expanded". Its definition is substituted for the function call, and the parameters used are substituted into the definition. Each time it is expanded in a different context, it can have different types in its type variables. So each time a function is expanded, it takes certain concrete types and returns a certain concrete type. It doesn't return a typeless thing that gets coerced at some later time.
Similarly, the read function is able to return whatever that is required to fit its type signature.
read :: Read a => String -> a
read
takes a String
as input, and can be used in any context that returns values of a type for which an instance of class Read
exists. When it is expanded during execution of a program, this type is known. It uses the particular definitions in the Read a
instance to parse the string into the correct type.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.