Reputation: 839
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
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
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 b
s.
Upvotes: 4