6

As an example, consider the trivial function

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

GHC complains that it cannot deduce (b ~ Int). The definition matches the signature in the sense that it returns something that is Integral (namely an Int). Why would/should GHC force me to use a more specific type signature?

Thanks

dying_sphynx
  • 1,136
  • 8
  • 17
crockeea
  • 21,651
  • 10
  • 48
  • 101

3 Answers3

17

Type variables in Haskell are universally quantified, so Integral b => b doesn't just mean some Integral type, it means any Integral type. In other words, the caller gets to pick which concrete types should be used. Therefore, it is obviously a type error for the function to always return an Int when the type signature says I should be able to choose any Integral type, e.g. Integer or Word64.

There are extensions which allow you to use existentially quantified type variables, but they are more cumbersome to work with, since they require a wrapper type (in order to store the type class dictionary). Most of the time, it is best to avoid them. But if you did want to use existential types, it would look something like this:

{-# LANGUAGE ExistentialQuantification #-}

data SomeIntegral = forall a. Integral a => SomeIntegral a

f :: a -> SomeIntegral
f x = SomeIntegral (3 :: Int)

Code using this function would then have to be polymorphic enough to work with any Integral type. We also have to pattern match using case instead of let to keep GHC's brain from exploding.

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

In the above example, you can think of x as having the type exists b. Integral b => b, i.e. some unknown Integral type.

hammar
  • 138,522
  • 17
  • 304
  • 385
  • 1
    Just in case, @hammar is referring to this funny error message when saying about "GHC's brain explosure" :) http://stackoverflow.com/a/213441/211906 – dying_sphynx Feb 29 '12 at 21:37
  • 1
    Yes, but no no no to ever using existential quantifiaction like this. The way to design this is not to: just have `f` return a plain old `Int` (or whatever concrete type is appropriate), which has more information than `Integral a => a`. Abstraction boundaries should occur over function arguments, not results. – luqui Mar 01 '12 at 01:22
2

The most general type of your function is

f :: a -> Int

With a type annotation, you can only demand that you want a more specific type, for example

f :: Bool -> Int

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

Ingo
  • 36,037
  • 5
  • 53
  • 100
0

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

This is different to most OOP languages, where a signature like this would mean that the function gets to choose what it returns. Apparently this confuses a few people...

MathematicalOrchid
  • 61,854
  • 19
  • 123
  • 220
  • It helps not to think of `f :: Foo a => Int -> a` as `Foo f(int);`, but as something more like `T f(int) where T : Foo;` (using C# syntax). – hammar Mar 02 '12 at 17:14