对于简单的代码,类型约束变得巨大且不可读
Type constraints become huge and unreadable for simple code
注意以下代码:
-- 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 交集函数,非常简单干净,除了 类型签名,它几乎比函数本身大 !有人建议我可以使用 "encapsule my needs" 的种类约束来减少冗长,但我找不到 "encapsules my needs" 的种类约束。 "My needs",在这种情况下,基本上都是"the type acts like a number should"。所以,在我看来,以下内容是有道理的:
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 来定义这样的 class,那应该是有原因的。没有 "Real" class 的原因是什么,如果定义这样的 class 是个坏主意,那么我的问题的正确解决方案是什么?
使用约束同义词:
{-# LANGUAGE ConstraintKinds #-}
type ConstraintSynonym f a = (
Foldable f,
Metric f,
Ord a,
Num (f a),
Fractional (f a),
Floating a)
与ConstraintKinds
一起,提升元组可用于表示约束的合取(而()
可指平凡满足的约束)。您现在可以使用 ConstraintSynonym
而不是注释中的大约束元组。
您的特定示例可以通过分别使用 ^-^
和 liftI2 (/)
而不是 -
和 /
来解决。这将约束减少到 (Foldable f, Metric f, Ord a, Floating a)
.
使用ConstraintKinds
你可以创建常量别名来减少集群:
type OrderedField a = (Ord a, Floating a)
type FoldableMetric f = (Foldable f, Metric f)
所以约束变为(FoldableMetric f, OrderedField a)
。你也可以在没有 ConstraintKinds
的情况下通过制作一个虚拟 class:
来做到这一点
class (Ord a, Floating a) => OrderedField a
instance (Ord a, Floating a) => OrderedField a
但这需要UndecidableInstances
.
向量并不是真正的数字。您不能为它们编写合理的 Ord
或 Real
实例(它们是否应该有一个 Num
实例值得商榷)。我认为将矢量和数字类型分开是一件好事。
注意以下代码:
-- 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 交集函数,非常简单干净,除了 类型签名,它几乎比函数本身大 !有人建议我可以使用 "encapsule my needs" 的种类约束来减少冗长,但我找不到 "encapsules my needs" 的种类约束。 "My needs",在这种情况下,基本上都是"the type acts like a number should"。所以,在我看来,以下内容是有道理的:
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 来定义这样的 class,那应该是有原因的。没有 "Real" class 的原因是什么,如果定义这样的 class 是个坏主意,那么我的问题的正确解决方案是什么?
使用约束同义词:
{-# LANGUAGE ConstraintKinds #-}
type ConstraintSynonym f a = (
Foldable f,
Metric f,
Ord a,
Num (f a),
Fractional (f a),
Floating a)
与ConstraintKinds
一起,提升元组可用于表示约束的合取(而()
可指平凡满足的约束)。您现在可以使用 ConstraintSynonym
而不是注释中的大约束元组。
您的特定示例可以通过分别使用 ^-^
和 liftI2 (/)
而不是 -
和 /
来解决。这将约束减少到 (Foldable f, Metric f, Ord a, Floating a)
.
使用ConstraintKinds
你可以创建常量别名来减少集群:
type OrderedField a = (Ord a, Floating a)
type FoldableMetric f = (Foldable f, Metric f)
所以约束变为(FoldableMetric f, OrderedField a)
。你也可以在没有 ConstraintKinds
的情况下通过制作一个虚拟 class:
class (Ord a, Floating a) => OrderedField a
instance (Ord a, Floating a) => OrderedField a
但这需要UndecidableInstances
.
向量并不是真正的数字。您不能为它们编写合理的 Ord
或 Real
实例(它们是否应该有一个 Num
实例值得商榷)。我认为将矢量和数字类型分开是一件好事。