我是哈斯凯尔的新手,我不时遇到一个问题,我将用几句话来描述。想象一下,我想为不同的度量声明不同的类型,所以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类的实例。
我想知道人们通常如何解决这个问题。
提前致谢!
答案 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
和软件......