decapo
decapo

Reputation: 839

Why can't haskell infer this type?

I have a simple type class that I'm having trouble working around.

class Entity a where
  eid :: a -> b

data Item = Item
  { itemId :: Int
  , itemName :: String
  }

instance Entity Item where
  eid i = itemId i

this gives me a compile error

main.hs:12:11: error:
    * Couldn't match expected type `b' with actual type `Int'
      `b' is a rigid type variable bound by
        the type signature for:
          eid :: forall b. Item -> b
        at main.hs:12:3-5
    * In the expression: itemId i
      In an equation for `eid': eid i = itemId i
      In the instance declaration for `Entity Item'
    * Relevant bindings include
        eid :: Item -> b (bound at main.hs:12:3)
   |
12 |   eid i = itemId i
   |           ^^^^^^^^  

I would think the compiler would be smart enough to know that for an Item I would like eid to return the Int type based on the info I've given but, I'm obviously missing something.

Upvotes: 1

Views: 159

Answers (2)

ath
ath

Reputation: 353

In Haskell it is not you who can decide what concrete type b stands for, but instead the caller of your eid function. By providing your implementation eid i = itemId i you fix the return type to be the same what itemId returns – an Int. Now the caller of eid can’t choose the type for b anymore, so this is why you get the error. It’s not really a lack of inference.

In a pure language the only thing a function can see is its arguments (and global constants). Here it is an a that is visible, but not a b. This means there exists no non-evil implementation for your function. Your function must return any type b that I (the caller) choose.

This signature won’t work, so you’ll have to change it. For example you could ask for the caller to provide a b and have eid :: a -> b -> b. In this case I am allowed to choose the type for b but I also have the obligation to provide you with a value of that type. Now your pure function can see a b and is able to return one.

Alternatively you could also simply specify that you want to return an Int: eid :: a -> Int.

Or you say: „Hey, I want to have a function that I provide with an a and I want it to compute any b a caller may choose – so let him provide me a function that can 'extract' that b”: eid :: a -> (a -> b) -> b.

Now the caller will have the obligation to pass in something like itemId. This means you have delayed the decision about what function actually can produce that b up until the point where eid is getting called.

Upvotes: 1

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476594

I would think the compiler would be smart enough to know that for an Item I would like eid to return the Int type based on the info I've given but, I'm obviously missing something.

Your signature eid :: a -> b promises that it can return a value for every type b. So it is not the instance that will decide what the return type is, it is the usage of the eid function that will determine this. So it means that one can use this as eid :: Entity a => a -> Integer for example, not per se Int.

If b fully depends on a, you can make use of functional dependencies [haskell-wiki]:

{-# LANGUAGE FunctionalDependencies, MultiParamTypeClasses #-}

class Entity a b | a -> b where
    eid :: a -> b

instance Entity Item Int where
    eid = itemId

You can also drop the | a -> b part if for a given a, there can be multiple bs.

Upvotes: 4

Related Questions