Reputation: 971
In the code below I have defined a data type F using existential quantification. I would like values of type F to hold functions that accept a single argument and produce, say, an Int as the result. The argument needs to implement a type class which I've called C, but left empty for now.
This is my first attempt at existential types. I am clearly doing something wrong as I don't seem to be able to create values of type F.
{-# LANGUAGE ExistentialQuantification #-}
class C a where
data F = forall a. C a => F (a->Int)
makeF :: F
makeF = F $ const 1
How can I fix this compile error?
No instance for (C a0) arising from a use of `F'
In the expression: F
In the expression: F $ const 1
In an equation for `makeF': makeF = F $ const 1
Upvotes: 2
Views: 171
Reputation: 12123
If you rewrite your F
definition as below it will work:
{-# LANGUAGE RankNTypes #-}
class C a where
data F = F (forall a. C a => a -> Int)
makeF :: F
makeF = F $ const 1
I'm trying to understand why myself:
Your original type says "there exists a
which is instance of C
, and we have function a -> Int
".
So to construct existential type, we have to state which a
we have:
{-# LANGUAGE ExistentialQuantification #-}
class C a where
data F = forall a. C a => F (a -> Int)
-- Implement class for Unit
instance C () where
makeF :: F
makeF = F $ (const 1 :: () -> Int)
These aren't exactly equivalent definitions:
data G = forall a . C a => G {
value :: a -- is a value! could be e.g. `1` or `()`
toInt :: a -> Int, -- Might be `id`
}
data G' = G' {
value' :: C a => a -- will be something for every type of instance `C`
toInt' :: C a => a -> Int, -- again, by parametericity uses only `C` stuff
}
Upvotes: 1
Reputation: 74344
The problem is that const 1
has type forall a . C a => a -> Int
. When you pass that to F
we lose the opportunity to ever talk about the type a
again excepting that it is an element of type C
.
Unfortunately, we've never determined what a
must be!
In particular, GHC handles existentials like this by passing in the dictionary for the typeclass C corresponding to whatever type actually ends up in the existential. Since we never provided GHC enough information to find that dictionary it gives us a type error.
So to fix this we must instantiate C
somewhere
instance C Int
and then pass a function which determines that the forgotten type a
actually is an instance of C
let x = F (const 1 :: Int -> Int)
Upvotes: 3