类型定义中的顺序约束

Seq constraint in type definition

此类型定义有效:

type Model<'t,'u when 't :> seq<'u> >(query: unit -> 't) = class end

然而,对我来说'u在这里是多余的,但是下一个定义:

type Model<'t when 't :> seq<_> >(query: unit -> 't) = class end

产生错误:

Anonymous type variables are not permitted in this declaration - F# Compiler (715)

紧凑

最紧凑的形式:

type Model<'t>(query:unit -> #seq<'t>) = class end

实例创建期间:

Query.users |> Model

产生错误:

Type constraint mismatch. The type unit -> (string * int) list
is not compatible with type unit -> 'a
F# Compiler (193)

可能是因为此处描述的缘故 。但无论如何,我不清楚错误描述,用 (string * int) list 代替 'a 有什么问题?

背景

真实类型 Model 是数据库查询的包装器,它实现 INotifyPropertyChanged 并包含 mutable 类型 Outcome:

的状态
type 't Outcome =
    | Empty
    | Loading
    | Success of 't
    | Fault   of string * string

需要 #seq<'t> 类型约束以通用方式检测 Empty 情况 Seq.isEmpty 由于查询可以 return seqlistarray

定义有问题

type Model<'t>(query:unit -> #seq<'t>) = class end

#引入了隐式类型参数,但是构造函数除了class之外不能有自己的类型参数。例如,您也不能这样定义:

type IntConverter(conv:'a -> int) = class end

因为构造函数不能有自己的自由类型参数'a.

不过,好消息是,即使您将定义更改为

type Model<'t>(query:unit -> seq<'t>) = class end

很容易接受 unit -> #seq<'t>:

类型的输入
let model f = Model(fun () -> upcast f())

我想只要你愿意添加那个辅助函数你就可以接近:

  type Model<'t>(query:unit -> seq<'t>) =
    class
      static member Unwrap<'s when 's :> seq<'t>> (cpar: unit -> 's) =
        cpar >> (fun (s: 's) -> (s :> seq<'t>))
      static member New (cpar) =
        new Model<'t>(Model.Unwrap<_> cpar)
    end

至少你可以使用类似(我认为)

Query.users |> Model.New

一旦我对实际的构造函数(重载)做了类似的事情,编译器就会说 "The type variable 's has been constrained to be type 'seq<'t>'."。因此,显然不同的规则适用于构造函数而不是静态方法(为什么我听起来很惊讶)。

正如@kvb 所解释的那样,问题在于您不能拥有泛型构造函数 - 因此您可以为整个 class 引入一个新的类型参数(就像您在第一个示例中所做的那样),或者return 类型的 query 需要只是 seq<'t>(编译器强制您在其他示例中这样做)。

如果您想将内容封装在 class 中,一个不错的技巧是将构造函数设为私有并添加一个静态 Create 方法,该方法可以具有您需要的额外通用参数:

type Model<'t> private(query:unit -> seq<'t>) = 
  static member Create(query:unit -> #seq<'t>) =
    Model(fun () -> query () :> _)