我从我的设计中提取了这些样本类型:
type SomeType = T1 of int | T2 of string
type Condition = Int | String
现在,出于单元测试目的以及从此处提取的其他原因(设计无法更改),我必须根据SomeType
标记创建Condition
值。
然而,我的第一次尝试没有编译:
let inline createSomeType' (someTypeVal : ^T, cond) =
match cond with
| Int -> T1 someTypeVal // warning FS0064: 'T restricted to "int"
| String -> T2 someTypeVal // error FS0001: expected "string" got "int"
将功能签名更改为createSomeType'< ^T >
也没有帮助:
error FS0001: expected "int" got 'T
error FS0001: expected "string" got 'T
然后我尝试重载:
type detail =
static member inline dispatch (someTypeVal : int) = T1 someTypeVal
static member inline dispatch (someTypeVal : string) = T2 someTypeVal
let inline createSomeType' (someTypeVal : ^T, cond) =
match cond with
| Int -> detail.dispatch someTypeVal // error FS0041: ambiguous overload
| String -> detail.dispatch someTypeVal // error FS0041: ambiguous overload
让我们消除歧视吧?不,添加类型注释会将someTypeVal
限制为int
,我们会回到我们开始的地方。
从C ++的角度来看,所有这些意味着F#编译器在模式匹配中的联合情况下不支持SFINAE。 我们可以使用这样的引用或动态检查:
A)
let inline createSomeType ((someTypeVal : obj), cond) =
match box someTypeVal with
| :? int when cond = Int -> T1(someTypeVal :?> int)
| :? string when cond = String -> T2(someTypeVal :?> string)
| _ -> failwith "something happened:("
B)
type Condition = Int = 0 | String = 1
type detail =
static member inline dispatch ((someTypeVal : int), (c : int)) =
if Condition.Int = enum<Condition>(c) then
T1 someTypeVal
else
failwith "something happened:("
static member inline dispatch ((someTypeVal : string), (c : int)) =
if Condition.String = enum<Condition>(c) then
T2 someTypeVal
else
failwith "something happened:("
detail.dispatch(123, int Condition.Int) // usage
然而,这并不简洁并抛出异常。
我应该如何实现createSomeType
函数,以便它在编译时完成所有工作?
P.S。这个问题是故意详细的,因为我无法在一个地方找到关于这个主题的大量信息,所以有人使用Google搜索可以节省时间而不会重复我的错误。
修改:
基本上,我需要一个方便的功能,它同时使用cond
和someTypeVal
以及^TVal -> Condition -> SomeType
等签名并编译时间类型解析。
正如@Gustavo所说,IIUC,如果没有编写N * M重载,它是不可能的:
您不能指望编译器检查包含的案例
cond
,因为这些案例都是价值观。
答案 0 :(得分:5)
正如评论中所述,我不清楚你想要达到什么目标。 什么决定? cont的VALUE或SomeType的TYPE?
您获得的所有错误对我来说都是完全合理的。在第一次尝试中,匹配的第一种情况假设您收到一个整数,因此它与整数结合。否则,该值应该加框,如下所示:
let createSomeType' (someTypeVal : obj, cond) =
match cond with
| Int -> T1 (someTypeVal :?> int )
| String -> T2 (someTypeVal :?> string)
在第二次尝试中,问题是F#中的重载并不像那样工作。它试图在调用站点解析,除非重载涉及静态约束,在这种情况下不会。
此代码可以使用:
type SomeType = T1 of int | T2 of string
type Condition = Int | String
type Detail = Detail with
static member ($) (Detail, someTypeVal : int) = T1 someTypeVal
static member ($) (Detail, someTypeVal : string) = failwith "something went wrong"; T2 someTypeVal
static member (%) (Detail, someTypeVal : string) = T2 someTypeVal
static member (%) (Detail, someTypeVal : int) = failwith "something went wrong"; T1 someTypeVal
let inline createSomeType' (someTypeVal : ^T, cond) =
match cond with
| Int -> Detail $ someTypeVal
| String -> Detail % someTypeVal
你可以期望编译器检查someTypeVal的类型,因为它是一个类型,它将在编译时检查,但你不能指望编译器检查{}中包含的情况。 {1}},因为这些案例都是值。
一个常见的误解是,判别联盟的情况代表了类型,实际上它们代表了单一类型的不同值。
如果您依赖类型,则根本不需要cond
。然后你的代码将是:
cond
我使用运算符而不是命名方法,因为它们会自动使用静态约束推断签名,但您也可以使用命名方法。