Home > database >  How can I use a class that has functional dependencies to define another class's constraint?
How can I use a class that has functional dependencies to define another class's constraint?

Time:05-06

{-# 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.

  • Related