在函数定义中使用实例模式

Using an instance pattern in a function definition

我想创建一个使用 "phantom typed" 个单位的运算符:

newtype Length (a::UnitLength) b = Length b deriving (Eq)
data UnitLength = Meter
            | KiloMeter
            | Miles
             deriving (Eq,Show)

class OperAdd a b c  where
  (<+>) :: a -> b -> c

instance  Num val => OperAdd (Length a val) (Length b val) (Length c val) where
  (<+>) (Length  la) (Length  lb) =  if a == b 
                                      then Length (la+lb) 
                                      else ...

为了避免每个单位重复实例声明,我想在实例声明中使用类型a bc来进行自动单位转换。

是否可以在我的运算符定义中使用 a bc

不,因为Haskell严格区分类型级变量和值级变量。您确定要在类型级别处理物理 单位 吗?物理 尺寸 ,这当然是有道理的(查看 units 包),但为什么不同单位长度需要不同类型,因为它们显然是等价的反正?好吧,这对于避免成本高昂的隐式单位转换可能是有意义的,但是为什么你要定义一个运算符来进行或多或少的隐式单位转换呢?

无论如何,您不能使结果类型 c 依赖于(值级别)if 语句。基本上你需要写出一个类型级别的 case/pattern 匹配;最直接的方法是使用多个实例:

instance  Num n => OperAdd (Length Metre n) (Length Metre n) (Length Metre val) where
  (<+>) (Length  la) (Length  lb) = Length (la+lb)
instance  Num n => OperAdd (Length Metre n) (Length KiloMetre n) (Length Metre n) where
  (<+>) (Length  la) (Length lb') = Length (la+lb*1000)
instance  Num n => OperAdd (Length Metre n) (Length Metre n) (Length KiloMetre n) where
  (<+>) (Length  la) (Length  lb) = Length $ (la+lb)/1000
...

singletons 可能会提供更通用的解决方案,看起来更类似于您的原始想法。