图包的annularWedge
函数不能取半径为0的内半径。您必须使用wedge
代替。
对我来说,内半径为0只是annularWedge
的简并情况,应该表现得像wedge
,所以我尝试将它们结合起来:
mywedge r2 r1 d a
| r1 == 0 = wedge r2 d a
| otherwise = annularWedge r2 r1 d a
当然,它不工作,我不知道错误是什么意思:
Non type-variable argument in the constraint: RealFloat (N t)
(Use FlexibleContexts to permit this)
When checking that ‘mywedge’ has the inferred type
mywedge :: forall t.
(RealFloat (N t), TrailLike t, V t ~ V2) =>
N t -> N t -> Direction V2 (N t) -> Angle (N t) -> t
事实上,annularWedge
和wedge
有不同的约束,这让我很惊讶:
annularWedge :: (TrailLike t, V t ~ V2, N t ~ n, RealFloat n) => n -> n -> Direction V2 n -> Angle n -> t
wedge :: (InSpace V2 n t, OrderedField n, TrailLike t) => n -> Direction V2 n -> Angle n -> t
那么我如何将这两个函数组合成一个相同的函数,接受内半径为0并且做正确的事情?
解决方案很简单。正如ErikR所说,将{-# LANGUAGE FlexibleContexts, TypeFamilies #-}
添加到文件的顶部。需要明确的是,添加这些扩展都是安全的(有些扩展,如重叠实例或不可判定的实例应该让您在启用它们之前暂停,但这些扩展大多是安全的)
虽然不明显,但wedge
签名的约束严格弱于annularWedge
签名的约束。
如果你好奇为什么签名看起来如此不同,请继续阅读…
对于初学者来说,约束条件,一旦你追踪到它们,并没有那么不同。让我们从wedge
开始。
(InSpace V2 n t, OrderedField n, TrailLike t)
看InSpace
的定义,你会发现它没有函数,它本质上就像一个同义词:(V a ~ v, N a ~ n, Additive v, Num n) => InSpace v n a
。然后,我们可以将InSpace V2 n t
扩展为(V t ~ V2, N t ~ n, Additive V2, Num n)
。类似地,OrderedField
实际上只是(Floating n, Ord n)
的简写。现在,wedge
的约束看起来像
(TrailLike t, V t ~ V2, N t ~ n, Additive V2, Num n, Floating n, Ord n)
然而,事实证明我们甚至可以删除Additive V2
约束,因为该实例是在Linear.Vector
中定义Additive
的,Num n
是多余的,假设Num n => Fractional n => Floating n
。这就给wedge
(TrailLike t, V t ~ V2, N t ~ n, Floating n, Ord n)
这看起来很像annularWedge
的约束。事实上,唯一的区别是wedge
约束(Floating n, Ord n)
,而annularWedge
约束RealFloat n
。在这一点上,约束非常相似,值得查看wedge
的代码。事实证明,wedge
使用_theta
,在HasTheta
中定义,相应的V2
实例使用arctan函数(这是在追逐更多依赖项后获得的)。
请遵循GHC给您的建议:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
import Diagrams
import Diagrams.TwoD.Arc
mywedge r2 r1 d a
| r1 == 0 = wedge r2 d a
| otherwise = annularWedge r2 r1 d a
一般来说,我更喜欢在处理时使用<=
比较使用浮点数,例如:
| r1 <= 1e-10 = wedge r2 d 1