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

Mar*_*ica 8 haskell typeclass

(对于奇怪的标题表示道歉,但我想不出更好的标题.)

对于个人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)

ale*_*tor 9

我想你正在寻找一个名为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博客中更好的描述


Chr*_*lor 8

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

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

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


aug*_*tss 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
    (#+) = ...
Run Code Online (Sandbox Code Playgroud)

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