我正在制作一些关于2D几何的基本模块,我决定用新类型包装坐标。这样我可以很容易地从极坐标(newtype Xy = Xy(Float,Float)
)中分辨出笛卡尔坐标(newtype Ra=Ra(Float,Float)
),也可能是复数;还要使它们成为Num
和Fractional
的实例,这样我就可以重载运算符。
但是,它们必须被定义为一对Float
还是一对Double
?在一些三角函数中,Double
在精度方面可能明显更好;我为Xy
新类型编写的许多函数我希望它们尽可能通用。
那么有没有办法用一对Fractional
类的数字来创建一个新类型?
此外,由于我发现编写Xy (0,0)
而不是简单地(0,0)
有点麻烦,我创建了·
运算符:
(·) a b = Xy (a,b)
但它似乎优先于其余部分,因此3+4·2+1
被评估为3+(Xy (4,2))+1
。
它也不会对函数声明,lambda表达式等有帮助......我仍然需要编写\Xy (a,b)->
。
感谢。
答案 0 :(得分:5)
您可以在新类型中使用类型参数:
newtype Xy n = Xy (n, n)
然后你可以编写适用于Fractional
类中的任何对的函数:
foo :: Fractional n => Xy n -> Xy n -> ...
如果您的操作只对双打有意义,您也可以使用Xy Double
等特定类型。
不幸的是,您不能只重复使用元组语法((a, b)
),但如果您编写的类型包含两个字段而不是包装一对,则它是可管理的:
data Xy n = Xy n n
现在您可以将Xy a b
写为值或模式。您甚至可以将其设为记录以获得方便的字段语法:
data Xy n = Xy { x, y :: n }
这与以前的版本一样,但也允许您使用x
和y
作为函数(即x :: Xy n -> n
)和模式。
答案 1 :(得分:3)
IMO你可能只是使用Double
,总是很难烘焙。在x86-64处理器上,Float
没有给你带来真正的好处 - 它需要相同的内存(因为每个值都需要一个64位指针来引用它),不会更快(甚至可能需要)额外的对齐开销) - 没有意义。 Double
很好,速度快,通常可以为†争取最佳精确度。
它肯定很多比存储类似OO的分数类的通用实例更好 - 这种多态性会导致相当大的重载。幸运的是,Haskell也真的避开了storing polymorphic values。
我也不认为将Xy
定义为仿函数在概念上是明智的,即Tikhon Jelvis建议的参数多态的类型。虽然这绝对是一种有效的方法,并且可以提供非常好的性能(这是linear library的工作方式),但这会错过传播的内容。数学向量最重要的是不是数字元组,而是a quantity that has both a direction and a magnitude。这种向量的实际坐标应仅被视为实现细节。
您也不想制作Num
等类的2D事物实例。这些类适用于numbers - 可以通过规范添加,减去和相乘的数量。你不能将矢量相乘,至少不能再给你一个矢量。向量的正确类是VectorSpace
,而不是Num
。
† 应该注意的是packed Float
数组确实可以节省大量内存,如果使用现代vectorised processor instructions,它们也可以实际上要快得多。但是这些优化在您讨论单个元组的级别上并不是真正可用的。