(对于奇怪的标题表示道歉,但我想不出更好的标题.)
对于个人Haskell项目,我希望得到"绝对值"(如频率)和相对值(如两个频率之间的比率)的概念.在我的上下文中,添加两个绝对值是没有意义的:一个可以添加相对值以产生新的相对值,并将相对值添加到绝对值以产生新的绝对值(同样对于减法).
我为这些定义了类型类:见下文.但是,请注意操作符##+
和#+
具有类似结构(同样适用于##-
和#-
).因此,我更愿意合并这些运算符,以便我有一个加法运算符,它添加一个相对值(同样是一个减法运算符,它会产生一个相对值). 更新:为了澄清,我的目标是统一我##+
和#+
一个单一的运营商.我的目标不是将它与existing(Num
)+
运算符统一起来.
但是,我不知道如何使用类型类来执行此操作.
问题:可以这样做,如果是,怎么做?或者我不应该尝试?
以下是我目前的情况:
{-# LANGUAGE MultiParamTypeClasses #-}
class Abs a where
nullPoint :: a
class Rel r where
zero :: r
(##+) :: r -> r -> r
neg :: r -> r
(##-) :: Rel r => r -> r -> r
r ##- s = r ##+ neg s
class (Abs a, Rel r) => AbsRel a r where
(#+) :: a -> r -> a
(#-) :: a -> a -> r
Run Code Online (Sandbox Code Playgroud)
我想你正在寻找一个名为Torsor的概念.光标由一组值,差异集和运算符组成,它们为值添加差值.此外,这组差异必须形成一个附加组,因此差异也可以加在一起.
有趣的是,躯干无处不在.常见的例子包括
等等
一个可能的Haskell定义是:
class Torsor a where
type TorsorOf a :: *
(.-) :: a -> a -> TorsorOf a
(.+) :: a -> TorsorOf a -> a
Run Code Online (Sandbox Code Playgroud)
以下是一些示例实例:
instance Torsor UTCTime where
type TorsorOf UTCTime = NominalDiffTime
a .- b = diffUTCTime a b
a .+ b = addUTCTime b a
instance Torsor Double where
type TorsorOf Double = Double
a .- b = a - b
a .+ b = a + b
instance Torsor Int where
type TorsorOf Int = Int
a .- b = a - b
a .+ b = a + b
Run Code Online (Sandbox Code Playgroud)
在最后一种情况下,请注意两组扭转器不需要是不同的组,这使得将相对值加在一起变得简单.
有关更多信息,请参阅Roman Cheplyakas博客中更好的描述
我认为你不应该试图统一这些运营商.减去两个向量并减去两个点是根本不同的操作.在类型系统中很难将它们表示为相同的事实并不是类型系统笨拙 - 这是因为这两个概念确实是不同的东西!
你正在使用的数学框架是仿射空间.
这些已在Haskell中的vector-space包中提供(cabal install vector-space
在命令提示符下执行).它们使用类型族来将向量(相对)类型与每个点(绝对)类型相关联,而不是使用多参数类型类.
这是一个最小的例子,展示了如何定义自己的绝对和相对数据类型及其交互:
{-# LANGUAGE TypeFamilies #-}
import Data.VectorSpace
import Data.AffineSpace
data Point = Point { px :: Float, py :: Float }
data Vec = Vec { vx :: Float, vy :: Float }
instance AdditiveGroup Vec where
zeroV = Vec 0 0
negateV (Vec x y) = Vec (-x) (-y)
Vec x y ^+^ Vec x' y' = Vec (x+x') (y+y')
instance AffineSpace Point where
type Diff Point = Vec
Point x y .-. Point x' y' = Vec (x-x') (y-y')
Point x y .+^ Vec x' y' = Point (x+x') (y+y')
Run Code Online (Sandbox Code Playgroud)
你有两个答案告诉你应该做什么,这是另一个答案告诉你如何做你要求的(这可能不是一个好主意).:)
class Add a b c | a b -> c where
(#+) :: a -> b -> c
instance Add AbsTime RelTime AbsTime where
(#+) = ...
instance Add RelTime RelTime RelTime where
(#+) = ...
Run Code Online (Sandbox Code Playgroud)
重载(#+)
使其非常灵活.太灵活了,IMO.唯一的限制是结果类型由参数类型决定(没有这个FD,操作符变得几乎无法使用,因为它没有任何约束).