9

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

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

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

3 回答 3

10

我认为您正在寻找一个称为Torsor的概念。一个 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

在最后一种情况下,请注意两组 Torsors 不需要是不同的集合,这使得将您的相对值加在一起很简单。

有关更多信息,请参阅Roman Cheplyakas 博客中更好的描述

于 2013-04-02T08:36:10.480 回答
9

我认为您不应该尝试统一这些运算符。减去两个向量和减去两个点是根本不同的操作。事实上,在类型系统中很难将它们表示为相同的东西并不是类型系统很尴尬——这是因为这两个概念真的是不同的东西!

您正在使用的数学框架是仿射空间

这些已经在向量空间包中的 Haskell 中可用(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')
于 2013-04-02T08:13:21.817 回答
9

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

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

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

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

于 2013-04-02T09:46:43.487 回答