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 ‘neg’
vfmap 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.
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.)