我想创建一个运算符来使用"幻像输入"单位:
var listA = new[] { 1, 2, 3 };
var listB = new[] { "a", "b", "c" };
var listC = new[] { 5f, 6f, 7f };
foreach(var n in Iterator.Enumerate(listA, listB, listC))
{
foreach(var obj in n)
{
Console.Write(obj.ToString() + ", ");
}
Console.WriteLine();
}
为避免重复每个单元的实例声明,我想在实例声明中使用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
和b
类型来进行自动单位转换。
是否可以在我的运营商定义中使用c
a
和b
?
答案 0 :(得分:2)
不,因为Haskell严格区分了类型级变量和值级变量。您确定要在类型级别处理物理单位吗?物理尺寸,这确实有意义(请查看例如units包),但为什么你需要不同类型的不同单位长度,因为它们显然是等同的?好吧,这可能是有意义的避免costy隐式单位转换,但为什么你定义一个运算符,它做了或多或少的隐式单位转换呢?
无论如何,您不能使结果类型c
依赖于(值级)if
语句。基本上你需要写出一个类型级case
/模式匹配;最直接的方法是使用多个实例:
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可能会出现更通用的解决方案,与原始想法更相似。