对于简单的代码,类型约束变得巨大且难以理解

Mai*_*tor 4 haskell design-patterns typeclass data-structures

请注意以下代码:

-- A n-dimensional axis aligned bounding box.
data AABB v a = AABB {
    aabbMin :: !(v a), 
    aabbMax :: !(v a)
    } deriving (Show)

-- `v` is a container, representing the number of dimensions. Ex:
-- type Box2DFloat = AABB V2 Float
-- type Box4DFloat = AABB V4 Float 

-- A n-dimensional ray.
data Ray v a = Ray {
    rayPos :: !(v a),
    rayDir :: !(v a)
    } deriving (Show)

-- Traces a n-d ray through a n-d box, returns 
-- the intersection indexes (tmin, tmax).
intersectAABB 
    :: (Foldable f, 
        Metric f, 
        Ord a, 
        Num (f a), 
        Fractional (f a), 
        Floating a) 
    => Ray f a 
    -> AABB f a 
    -> [a]
intersectAABB (Ray rayPos rayDir) (AABB aabbMin aabbMax) 
    = [tmin, tmax] where
        t1   = (aabbMin - rayPos) / rayDir
        t2   = (aabbMax - rayPos) / rayDir
        tmin = foldr1 max $ liftI2 min t1 t2
        tmax = foldr1 min $ liftI2 max t1 t2
Run Code Online (Sandbox Code Playgroud)

这是一个常见的Ray→AABB交叉函数,它非常简单干净,除了类型签名,它几乎比函数本身大!有人建议我可以使用"封装我的需求"的类型约束来减少冗长,但是我找不到一种"密封我的需要"的类型约束.在这种情况下,"我的需要"基本上是"类型就像一个数字应该".所以,在我看来,以下是有道理的:

class Real a where
    ... anything I want a real number to do ...

instance Real Float where
    ...

instance (Real a) => Real (V2 a) where
    ...

instance (Real a) => Real (V3 a) where
    ...

type AABB a = V2 a
type Ray a  = V2 a

type Box2DFloat = AABB (V2 Float)
type Box4DFloat = AABB (V4 Float)
type Ray2D a = Ray (V2 a)
type Ray3DRatio = Ray (V3 Ratio)
... etc ...
Run Code Online (Sandbox Code Playgroud)

这样,我的签名就会变成,简单地说:

intersectAABB :: (Real r, Real s) => Ray r -> AABB r -> [s]
Run Code Online (Sandbox Code Playgroud)

哪个看起来好多了.但是,如果没有人使用Haskell打扰定义这样的类,那么应该有一个理由.没有"真正"类的原因是什么,如果定义这样一个类是个坏主意,那么我的问题的正确解决方案是什么?

And*_*ács 6

使用约束同义词:

{-# LANGUAGE ConstraintKinds #-}

type ConstraintSynonym f a = (
  Foldable f,
  Metric f,
  Ord a, 
  Num (f a), 
  Fractional (f a), 
  Floating a)
Run Code Online (Sandbox Code Playgroud)

使用ConstraintKinds,提升元组可用于表达约束的结合(并且()可以指代平凡满足的约束).您现在可以ConstraintSynonym在注释中使用而不是大元组的约束.