{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
class Fractional f => Point3D p f | p -> f where
x :: p -> f
y :: p -> f
z :: p -> f
class (Fractional fr, Point3D p fr, Foldable f) => HasPoints3D ps p f where
points :: ps -> f p
I made Point3D
class to cover diverse implementations of 3D Point. And I made HasPoints3D
class to cover all things that have some points. The second type variable f
in Point3D
definition is determined by the first type variable p
. So, I think this definition of HasPoints3D
have no problem, because fr
is can determined by p
.
I know that it may cause typechecker loop. So, I added {-# LANGUAGE UndecidableInstances #-}
. But it is not working, it says Not in scope: type variable ‘fr’
. Is there another haskell extension to make this work? Or haskell just can't deal with this situation?
CodePudding user response:
Personally, I tend to avoid functional dependencies, preferring to use type families instead when possible. I think it's often simpler, and avoids issues like this one.
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
class Fractional (F p) => Point3D p where
type F p
x :: p -> F p
y :: p -> F p
z :: p -> F p
class (Fractional (F p), Point3D p, Foldable f) => HasPoints3D ps p f where
points :: ps -> f p
CodePudding user response:
I agree with chi that Points3D
should probably not be a multi-parameter class in the first place. But if you can't do anything about that, you can still just use an associated type family in HasPoints3D
:
{-# LANGUAGE ..., TypeFamilies, FlexibleContexts #-}
import Data.Kind (Type)
class ( Fractional (DirectionalComponent ps p f)
, Point3D p (DirectionalComponent ps p f)
, Foldable f ) => HasPoints3D ps p f where
type DirectionalComponent ps p f :: Type
points :: ps -> f p
BTW, I find the Point3D
abstraction highly dubious. Why do you need a typeclass for this? This sounds like it behaves like just one concrete type
data V3 s = V3 {x,y,z :: !s}
What would likely make much more sense to not require specifically three dimensions with any particular coordinate axes, but instead be abstract over the vector space you're working in.
Similarly, why do you need the HasPoints3D
class? Why not just use f p
as it is, with suitable constraints on f
and p
separatly, as needed?
Suggest reading: Rules of thumb for when to use a type class.