这个功能:
let convert (v: float<_>) =
match v with
| :? float<m> -> v / 0.1<m>
| :? float<m/s> -> v / 0.2<m/s>
| _ -> failwith "unknown"
产生错误
'float<'u>' 类型没有任何适当的子类型,不能用作类型测试或运行时强制的来源。
有什么方法可以匹配计量单位?
这个功能:
let convert (v: float<_>) =
match v with
| :? float<m> -> v / 0.1<m>
| :? float<m/s> -> v / 0.2<m/s>
| _ -> failwith "unknown"
产生错误
'float<'u>' 类型没有任何适当的子类型,不能用作类型测试或运行时强制的来源。
有什么方法可以匹配计量单位?
正如@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
对于像and这样的普通类型float
,您还可以使用重载成员(在某些 F# 类型中声明),但这不适用于度量单位,因为在 F# 编译器擦除单位信息后签名将相同。
你的方法有两个问题。首先,当您在函数定义中使用下划线时,这与使用新类型变量相同,因此您的定义等效于以下内容:
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
是 type float<'u>
,并且float<'u>
没有适当的子类型,因此进行类型测试以确定它是 afloat<m>
还是任何其他类型是没有意义的。
您可以尝试通过首先装箱v
到一个对象然后进行类型测试来解决这个问题。例如,如果您有 alist<'a>
并且想查看它是否是 a ,这将起作用,list<int>
因为有关泛型对象的完整类型信息在运行时被跟踪,包括泛型类型参数(值得注意的是,这与其他一些运行时(如 Java 的工作方式)不同) . 不幸的是,F# 度量单位在运行时被删除,所以这在这里不起作用 - 系统无法在给定盒装表示的情况下推断正确的度量类型,因为在运行时该值只是一个普通的float
- F# 的系统在这方面,度量单位实际上与 Java 处理泛型类型的方式非常相似。
顺便说一句,您尝试做的事情似乎很可疑-在度量单位中通用的函数不应该根据度量类型做不同的事情;它们应该是适当的参数。你到底想达到什么目的?它当然看起来不像是对应于物理现实的操作,而物理现实是 F# 度量类型的基础。
请参阅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>