重载时静态解析的类型参数推断错误

时间:2015-10-06 18:41:33

标签: f#

我从我的设计中提取了这些样本类型:

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搜索可以节省时间而不会重复我的错误。

修改

基本上,我需要一个方便的功能,它同时使用condsomeTypeVal以及^TVal -> Condition -> SomeType等签名并编译时间类型解析。

正如@Gustavo所说,IIUC,如果没有编写N * M重载,它是不可能的:

  

您不能指望编译器检查包含的案例   cond,因为这些案例都是价值观。

1 个答案:

答案 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

我使用运算符而不是命名方法,因为它们会自动使用静态约束推断签名,但您也可以使用命名方法。