{-# LANGUAGE GeneralizedNewtypeDeriving #-}
newtype Bytes = B Double deriving (Num,Fractional,Show)
newtype Grams = G Double deriving (Num,Fractional,Show)
newtype Time = Sec Double deriving (Num,Fractional,Show)
data S a = M a | K a | U a deriving Show
mkM :: Num a => a -> S a
mkM a = M a
mkK :: Num a => a -> S a
mkK a = K a
mkU :: Num a => a -> S a
mkU a = U a
eval :: Num a => S a -> a
eval (M a) = a * (10^6)
eval (K a) = a * (10^3)
eval (U a) = a
m2k :: Num a => S a-> S a
m2k (M a) = K(a*1000)
k2m :: Fractional a => S a-> S a
k2m (K a) = (M (a/1000.0))
x = mkK $ B 100
y = m2k x
上面的例子在ghci中加载,但y
方程在运行时失败。这是正确的行为,但我想让这个程序在编译时失败。m2K
函数不应该接收任何(S a)
参数,而只接收从M
数据构造函数生成的表达式。在Haskell中有没有简单的方法来实现这一点?
1条答案
按热度按时间mum43rcc1#
作为一般规则,Haskell在编译时检查 * 类型 *,而不是 * 值 *。如果你有两个值,比如你的例子中的
K (B 100)
和M (B 100)
,它们是相同的 * 类型 *(即S Bytes
),那么没有直接的方法来编写一个函数,在编译时接受其中的一些值并拒绝其他值。如果你想限制一个函数在编译时可以接受的值的“类型”,正确的方法是在类型系统级别对其建模。如果
m2k
应该只取M
s,那么M
s等等需要是它们自己的类型:和
m2k
以及类似的应该在它们的类型签名中反映这一点:如果你仍然希望能够编写像
eval
这样的函数来处理M
s、K
s或U
s中的任何一个,你可以在这三个不同的类型上引入一个sum类型:对
eval
进行相应的更改: