此功能:
let convert (v: float<_>) =
match v with
| :? float<m> -> v / 0.1<m>
| :? float<m/s> -> v / 0.2<m/s>
| _ -> failwith "unknown"
产生错误
类型'float&lt;'u&gt;'没有任何正确的子类型,也不能用作类型测试或运行时强制的来源。
有没有办法模拟匹配度量单位?
答案 0 :(得分:7)
正如 @kvb 详细解释的那样,问题是度量单位是类型的一部分。这意味着float<m>
与float<m/s>
的类型不同(不幸的是,此信息在运行时不会作为值的一部分存储)。
因此,您实际上正在尝试编写一个可以使用两种不同类型输入的函数。干净的功能解决方案是声明一个可以保存第一种类型或第二种类型的值的区别联合:
type SomeValue =
| M of float<m>
| MPS of float<m/s>
然后你可以使用普通的模式匹配来编写函数:
let convert v =
match v with
| M v -> v / 0.1<m>
| MPS v -> v / 0.2<m/s>
您需要将值显式地包装到区分联合值中,但这可能是直接执行此操作的唯一方法(不对程序结构进行更大的更改)。
对于int
和float
这样的普通类型,您也可以使用重载成员(以某些F#类型声明),但这不适用于度量单位,因为签名将是在F#编译器擦除单元信息后也一样。
答案 1 :(得分:3)
您的方法存在两个问题。首先,当您在函数定义中使用下划线时,这与使用新类型变量相同,因此您的定义等同于以下内容:
let convert (v: float<'u>) = //'
match v with
| :? float<m> -> v / 0.1<m>
| :? float<m/s> -> v / 0.2<m/s>
| _ -> failwith "unknown"
错误消息告诉您的是编译器知道v
类型为float<'u>
,而float<'u>
没有正确的子类型,因此进行类型测试没有意义确定它是float<m>
还是其他任何类型。
您可以尝试通过先将v
装入对象然后进行类型测试来解决此问题。例如,如果您有一个list<'a>
并希望查看它是否是list<int>
,这将有效,因为在运行时跟踪有关通用对象的完整类型信息,包括泛型类型参数(特别是,这是不同于其他一些像Java工作的运行时)。不幸的是,F#测量单位在运行时被删除,所以这在这里不起作用 - 系统无法在给定盒装表示的情况下推断出正确的度量类型,因为在运行时该值只是普通的{{1}在这方面,F#的度量单位系统实际上与Java处理泛型类型的方式非常相似。
顺便说一下,你正在尝试做的事情似乎非常可疑 - 在度量单位中通用的函数不应该根据度量类型的不同而做不同的事情;它们应该是适当参数化的。你到底想要达到什么目的?它当然看起来不像是一个与物理现实相对应的操作,它是F#测量类型的基础。
答案 2 :(得分:0)
请参阅http://msdn.microsoft.com/en-us/library/dd233243.aspx上的运行时单位部分。
我同意@kvb,我认为解决这个问题的最佳方法是传递一个对象。
我想用你的代码结构做什么:
let convert (v: float<_>) =
match v with
| :? float<m> -> v<m>
| :? float<inches> -> v * 2.54 / 100.0<m>