Polymorphic signature for non-polymorphic function: why not?

As an example, consider a trivial function

f :: (Integral b) => a -> b fx = 3 :: Int 

GHC complains that it cannot output (b ~ Int). The definition corresponds to the signature in the sense that it returns what is integral (namely, Int). Why / would the GHC force me to use a more specific type signature?

thanks

+6
source share
3 answers

Haskell type variables are universally evaluated, so Integral b => b not only means some type of Integral , it means any type of Integral . In other words, the caller chooses which specific types to use. Therefore, it is obvious that the error type always returns Int when the type signature says that I should select any Integral type, for example. Integer or Word64 .

There are extensions that allow you to use existentially quantified type variables , but they are more cumbersome to work because they require a shell type (to store a type dictionary). In most cases, it is best to avoid them. But if you want to use existential types, it will look something like this:

 {-# LANGUAGE ExistentialQuantification #-} data SomeIntegral = forall a. Integral a => SomeIntegral a f :: a -> SomeIntegral fx = SomeIntegral (3 :: Int) 

Code using this function should then be polymorphic enough to work with any type of Integral . We must also match patterns using case instead of let so that the GHC brain does not explode.

 > case f True of SomeIntegral x -> toInteger x 3 > :t toInteger toInteger :: Integral a => a -> Integer 

In the above example, you might think that x is of type exists b. Integral b => b exists b. Integral b => b , i.e. the unknown type is Integral .

+17
source

The most common type of your function is

 f :: a -> Int 

With type annotation, you can only require a more accurate type, e.g.

 f :: Bool -> Int 

but you cannot declare a less specific type. A system like Haskell does not allow you to make promises that are not guaranteed by your code.

+2
source

As others have said, in Haskell, if a function returns a result of type x , it means that the caller decides what the actual type is. Not the function itself. In other words, the function must be able to return any possible type that matches the signature.

This differs from most OOP languages, where a similar signature means that the function receives a choice of what it returns. Apparently, this confuses a few people ...

0
source

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


All Articles