如何在Haskell中添加/减去绝对值和相对值的运算符

时间:2013-04-02 06:56:26

标签: haskell typeclass

(为奇怪的标题道歉,但我想不出更好的标题。)

对于个人Haskell项目,我希望得到“绝对值”(如频率)和相对值(如两个频率之间的比率)的概念。在我的上下文中,添加两个绝对值是没有意义的:可以添加相对值以生成新的相对值,并将相对值添加到绝对值以生成新的绝对值(对于减法也是如此)。

我为这些定义了类型类:见下文。但请注意,运算符##+#+具有相似的结构(同样适用于##-#-)。因此,我更愿意合并这些运算符,以便我有一个加法运算符,它添加一个相对值(同样是一个减法运算符,它会产生一个相对值)。 更新:为了澄清,我的目标是将我的##+#+统一到一个运营商中。我的目标是将其与现有的(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

3 个答案:

答案 0 :(得分:9)

我认为你正在寻找一个名为 Torsor 的概念。光标由一组值,差异集和运算符组成,它们为值添加差值。此外,这组差异必须形成一个附加组,因此差异也可以加在一起。

有趣的是,躯干无处不在。常见的例子包括

  • 点数和矢量
  • 日期和日期差异
  • 文件和差异

等。

一个可能的Haskell定义是:

 class Torsor a where
    type TorsorOf a :: *
    (.-) :: a -> a -> TorsorOf a
    (.+) :: a -> TorsorOf a -> a

以下是一些示例实例:

 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

在最后一种情况下,请注意两组扭转器不需要是不同的组,这使得将相对值加在一起变得简单。

有关详细信息,请参阅a much nicer description in Roman Cheplyakas blog

答案 1 :(得分:8)

我认为你不应该试图统一这些运营商。减去两个向量并减去两个点是根本不同的操作。在类型系统中很难将它们表示为相同的事实并不是类型系统笨拙 - 这是因为这两个概念确实是不同的东西!

您正在使用的数学框架是affine space

这些在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')

答案 2 :(得分:8)

你有两个答案告诉你应该做什么,这是另一个答案,告诉你如何做你要求的(这可能不是一个好主意)。 :)

class Add a b c | a b -> c where
    (#+) :: a -> b -> c

instance Add AbsTime RelTime AbsTime where
    (#+) = ...
instance Add RelTime RelTime RelTime where
    (#+) = ...

(#+)的重载使其非常灵活。太灵活了,IMO。唯一的限制是结果类型由参数类型决定(没有这个FD,操作符变得几乎无法使用,因为它没有任何约束)。