如何让Haskell在编译时限制函数m2k和k2m的参数?

k10s72fa  于 2023-10-19  发布在  其他
关注(0)|答案(1)|浏览(153)
{-# 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中有没有简单的方法来实现这一点?

mum43rcc

mum43rcc1#

作为一般规则,Haskell在编译时检查 * 类型 *,而不是 * 值 *。如果你有两个值,比如你的例子中的K (B 100)M (B 100),它们是相同的 * 类型 *(即S Bytes),那么没有直接的方法来编写一个函数,在编译时接受其中的一些值并拒绝其他值。
如果你想限制一个函数在编译时可以接受的值的“类型”,正确的方法是在类型系统级别对其建模。如果m2k应该只取M s,那么M s等等需要是它们自己的类型:

newtype M a = M a
newtype K a = K a
newtype U a = U a

m2k以及类似的应该在它们的类型签名中反映这一点:

m2k :: Num a => M a -> K a
m2k (M a)  = K (a*1000)

k2m :: Fractional a => K a -> M a
k2m (K a) = M (a/1000)

如果你仍然希望能够编写像eval这样的函数来处理M s、K s或U s中的任何一个,你可以在这三个不同的类型上引入一个sum类型:

data S a = SM (M a) | SK (K a) | SU (U a)

eval进行相应的更改:

eval :: Num a => S a -> a
eval (SM (M a)) = a * (10^6)
eval (SK (K a)) = a * (10^3)
eval (SU (U a)) = a

相关问题