2010-04-19 178 views
3

此功能:模式匹配

let convert (v: float<_>) = 
    match v with 
    | :? float<m> -> v/0.1<m> 
    | :? float<m/s> -> v/0.2<m/s> 
    | _ -> failwith "unknown" 

产生错误

类型“浮动<‘U>’不具有任何适当的子类型,并且不能被用作类型测试或运行时强制的来源。

有没有办法如何模式匹配度量单位?

回答

6

由于@kvb详细解释,问题是度量单位是类型的一部分。这意味着float<m>是与float<m/s>不同的类型(并且不幸的是,该信息不作为运行时值的一部分存储)。

所以,你实际上试图编写一个函数,可以使用两种不同类型的输入。清洁功能的解决方案是定义一个可识别联合,可容纳无论是第一类还是第二类的值:

let convert v = 
    match v with 
    | M v -> v/0.1<m> 
    | MPS v -> v/0.2<m/s> 

您:

type SomeValue = 
    | M of float<m> 
    | MPS of float<m/s> 

然后你就可以使用普通的模式匹配写函数我们需要明确地将这些值包装到区分的联合值中,但这可能是直接完成此操作的唯一方法(不需要对程序结构进行较大的更改)。对于正常类型,如intfloat,您也可以使用重载成员(在某些F#类型中声明),但这对于度量单位不起作用,因为在F#编译器擦除后,签名将相同单位信息。

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>,那么这是行得通的,因为有关通用对象的完整类型信息在运行时被跟踪,包括泛型类型参数(值得注意的是,这与其他一些运行时工作)。不幸的是,F#测量单位在运行时会被擦除,所以在这里不起作用 - 由于在运行时这个值只是一个普通的系统,所以系统无法推断正确的测量类型 - F#的系统对于度量单位而言,在这方面与Java处理泛型类型的方式实际上非常相似。另一方面,你试图做的事似乎相当可疑 - 在度量单位中通用的函数不应该根据度量类型做什么不同的事情;他们应该是正确的参数。你究竟想达到什么目的?它看起来不像一个与物理现实相对应的操作,这是F#测量类型的基础。

+0

嗨, 感谢您的回答。我将不得不重新思考我的代码。 一条评论: 为什么在使用Microsoft.FSharp.Math.SI模块时,单位的值不会简化为<1/s>? – 2010-04-19 10:16:10

+1

@奥德里奇 - 我不清楚为什么这不是简单的(我的阅读规范表明它应该是),但F#知道这些措施是等价的(例如,你可以写'let(x:float <1/s>)= 1.0 '和编译器将允许它)。有可能像Pa这样的缩写并不总是为了显示目的而展开。 – kvb 2010-04-19 10:57:49

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>