简体   繁体   中英

How fromIntegral or read works

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM