请注意以下代码:
那么,我的签名将变得非常简单:
-- 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
这是一个通常的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 ...
那么,我的签名将变得非常简单:
intersectAABB :: (Real r, Real s) => Ray r -> AABB r -> [s]
这看起来好多了。但是,如果没有使用Haskell的人费心定义这样一个类,那么一定存在某种原因。为什么没有"Real"类?如果定义这样一个类是个坏主意,那么解决我的问题的正确方法是什么?