Haskell中不同类型的乘法

时间:2011-03-15 20:25:34

标签: haskell types

我是哈斯凯尔的新手,我不时遇到一个问题,我将用几句话来描述。想象一下,我想为不同的度量声明不同的类型,所以Haskell类型系统会在我的公式中找到错误:

newtype Dist = Km Float
newtype Time = H Float
newtype Velocity = KmH Float

(/) :: Dist → Time → Velocity
(Km d) / (H t) = KmH (d / v)

(*) :: Velocity → Time → Dist
(KmH v) * (H t) = Km (v * t)

所以,每当我尝试在我的公式中使用错误的度量单位时,编译器就会咬人。

问题在于我不能像这样实现ad-hoc polimorphism。使用此代码,我带来了歧义 - 编译器可以区分我的运算符*和Prelude中定义的运算符。由于我需要不同类型的参数,因此也无法声明Num类的实例。

我想知道人们通常如何解决这个问题。

提前致谢!

4 个答案:

答案 0 :(得分:6)

如果需要,您可以通过

隐藏常用(*)
import Prelude hiding((*))

或隐藏所有Num

import Prelude hiding(Num(..))

然后你可以定义你自己的乘法,可能是沿着

class Mul a b c | a b -> c, b c -> a, c a -> b where
    (*) :: a -> b -> c

答案 1 :(得分:5)

您可以尝试重新制定单位系统。尝试这样的事情:

data Unit = Unit String
          | Unit :/: Unit
          | Unit :*: Unit
          | Unit :^: Int
          deriving (Show,Eq)

instance Num Unit where
  -- Insert reasonable definition here
  x * y = ...

data UnitNum n = UN n Unit

instance Num (Num n) => UnitNum n where
  UN n u + Un k u' | u == u' = UN (n+k) u
                   | otherwise = error ...
  -- insert other definitions here.

km,h,kmh :: Unit

km = Unit "km"
h = Unit "h"
kmh = km / h

编辑:

与此类似的东西,但dimensional包中实现了完全不同的东西。阅读源代码,它是有文化的Haskell,非常好理解。这段代码应该足以满足大多数科学应用。

答案 2 :(得分:3)

通常的方法是创建一个不同的运算符来乘以你的类型 - 已经采用了普通*。您可以使用字符!#$%&*+./<=>?@\^|-~的任意组合定义自己的运算符。所以你可以使用|*|(TIE Fighter算子)和|/|之类的东西。

答案 3 :(得分:2)

我这样做的方式(为了避免维度包的类型级复杂性)基本上是你的新类型解决方案,但有相当数量的辅助函数。

class Units a where
    wrap :: Double -> a
    unWrap :: a -> Double

instance Units Double where
    wrap = id
    unWrap = id

inWrap :: (Units a) => (Double -> Double) -> a -> a
inWrap f = wrap . f . unWrap

newtype Years = Years Double deriving (Typeable, Show, Eq, Ord)
instance Units Years where
    wrap = Years
    unWrap (Years x) = x

newtype a :/: b = Per a deriving (Typeable, Show, Eq, Ord)
instance Units a => Units (a :/: b) where
    wrap = Per . wrap
    unWrap (Per x) = unWrap x

perYears :: a -> a :/: Years
perYears = Per

class Additive a where
    (+%) :: a -> a -> a
    negU :: a -> a
    (-%) :: a -> a -> a
    x -% y = x +% negU y

instance Units a => Additive a
    where x +% y = wrap $ unWrap x + unWrap y
          negU = inWrap negate

class ScalarMult f where
    (.*) :: f -> Double -> f
class ScalarAdd f where
    (.+) :: f -> Double -> f

instance Units a => ScalarAdd a where
    f .+ v = inWrap (+ v) f
instance Units a => ScalarMult a where
    f .* v = inWrap (* v) f

和软件......