haskelltype-variables

Cannot seem to use a type expression with a type variable in an instance declaration with a function needing an explicit type


I cannot find anyway in Haskell to specify the type of the call to 'neg':

instance Arith (V3 e) where neg x = vfmap (neg :: e->e)  x 

(V3 e) and e are both instances of Arith. Here I want to call the 'neg' already defined for the type 'e'. But this requires an explicit type on the 'neg' call and no expression can resolve the type? If use a specific instance of 'e', it si fine.

vfmap (neg :: Dist->Dist ) x -- this works (but is not general enough) vfmap (neg :: e->e) x -- No instance for (Arith e1) arising from a use of ‘negvfmap neg e -- Ambiguous type variable ‘e0’ arising from a use of ‘neg’ prevents the constraint ‘(Arith e0)’ from being solved. vfmap (neg :: Arith e => e->e) x -- ditto

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, InstanceSigs #-}
data Dist = Inch Float deriving (Show)

class Arith a where
   neg :: a->a

instance Arith Dist where
   neg (Inch x) = Inch (-x)

data V2 e = V2 e e    deriving (Show) 
data V3 e = V3 e e e  deriving (Show)

class VMap c e where
   vfmap :: (e->e)->c->c

instance VMap (V2 e) e where
   vfmap f (V2 x1 x2) = V2 (f x1) (f x2)
instance VMap (V3 e) e where
   vfmap f (V3 x1 x2 x3) = V3 (f x1) (f x2) (f x3)

-- 2 & 3 point vectors should also be Arith
instance Arith (V2 Dist) where 
   neg x = vfmap (neg :: Dist->Dist) x -- works, but must have type on neg

instance Arith (V3 e) where 
   neg x = vfmap (neg :: Arith e => e->e)  x -- nothing here seems to work

vfmap can be applied to a (V2 e) or a (V3 e), either vector type for a vector of any Arith element type.

This won't seem to compile when the element type is a type variable, e.g.

• Ambiguous type variable ‘e0’ arising from an expression type signature prevents the constraint ‘(Arith e0)’ from being solved. Probable fix: use a type annotation to specify what ‘e0’ should be.


Solution

  • The issue is that in Haskell, type variables aren't scoped: that is, if you define instance Arith (V3 e), you can't use e inside the instance; if you try to, GHC will interpret it as being a completely separate type variable. Luckily, you can use {-# LANGUAGE ScopedTypeVariables #-} to enable scoped type variables. If you do that, you'll also find you need to add an extra Arith e => constraint; adding this will allow it to compile successfully.

    (Aside: when dealing with MultiParamTypeClasses, {-# LANGUAGE FunctionalDependencies #-} is also incredibly useful; I would personally use it in a case like this, as it removes the need for explicit type declarations for neg. The idea is that you define instead class Functor c e | c -> e, which basically means that the type for c determines the type for e as well. I won't describe it here, but I would highly encourage you to look it up.)