6

这个功能:

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>' 类型没有任何适当的子类型,不能用作类型测试或运行时强制的来源。

有什么方法可以匹配计量单位?

4

3 回答 3

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对于像and这样的普通类型float,您还可以使用重载成员(在某些 F# 类型中声明),但这不适用于度量单位,因为在 F# 编译器擦除单位信息后签名将相同。

于 2010-04-19T14:33:26.457 回答
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是 type float<'u>,并且float<'u>没有适当的子类型,因此进行类型测试以确定它是 afloat<m>还是任何其他类型是没有意义的。

您可以尝试通过首先装箱v到一个对象然后进行类型测试来解决这个问题。例如,如果您有 alist<'a>并且想查看它是否是 a ,这将起作用,list<int>因为有关泛型对象的完整类型信息在运行时被跟踪,包括泛型类型参数(值得注意的是,这与其他一些运行时(如 Java 的工作方式)不同) . 不幸的是,F# 度量单位在运行时被删除,所以这在这里不起作用 - 系统无法在给定盒装表示的情况下推断正确的度量类型,因为在运行时该值只是一个普通的float- F# 的系统在这方面,度量单位实际上与 Java 处理泛型类型的方式非常相似。

顺便说一句,您尝试做的事情似乎很可疑-在度量单位中通用的函数不应该根据度量类型做不同的事情;它们应该是适当的参数。你到底想达到什么目的?它当然看起来不像是对应于物理现实的操作,而物理现实是 F# 度量类型的基础。

于 2010-04-19T09:08:36.843 回答
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>
于 2013-05-15T16:13:49.247 回答