我如何使用具有函数依赖性的 class 来定义另一个 class 的约束?

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

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

我制作了 Point3D class 以涵盖 3D 点的不同实现。我做了 HasPoints3D class 来涵盖所有有一些要点的东西。 Point3D定义中的第二个类型变量f由第一个类型变量p决定。所以,我觉得HasPoints3D这个定义没有问题,因为fr是可以由p.

决定的

我知道这可能会导致 typechecker loop。所以,我添加了 {-# LANGUAGE UndecidableInstances #-}。但它不起作用,它说 Not in scope: type variable ‘fr’。是否有另一个 haskell 扩展来完成这项工作?还是haskell就是无法应对这种情况?

就我个人而言,我倾向于避免函数依赖,而是尽可能使用类型族。我认为它通常更简单,并且避免了类似这样的问题。

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

我同意 chi 的观点,Points3D 一开始可能不应该是 multi-parameter class。但是,如果您对此无能为力,您仍然可以在 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

顺便说一句,我发现 Point3D 抽象非常可疑。为什么你需要一个类型class?这听起来像是一种具体类型

data V3 s = V3 {x,y,z :: !s}

什么对 可能更有意义,而不是 需要具有任何特定坐标轴的特定三个维度,而是对您正在使用的 vector space 进行抽象.

同样,为什么需要HasPoints3Dclass?为什么不按原样使用 f p,根据需要分别对 fp 施加适当的约束?

建议阅读:Rules of thumb for when to use a type class.