JsonProvider给了我无法统一的不同类型

时间:2018-09-12 14:03:59

标签: json types f# f#-data

我正在使用JSON type provider加载我创建的JSON文件。类型提供程序的最小输入如下所示:

{
  "conv1": {
    "weight": {
      "shape": [ 64, 3, 7, 7 ],
      "data": [ 1e-30, -0.01077061053365469 ]
    }
  },
  "bn1": {
    "eps": 1e-05,
    "weight": {
      "shape": [ 64 ],
      "data": [ 1e-30, 0.2651672959327698 ]
    },
    "bias": {
      "shape": [ 64 ],
      "data": [ 1e-30, 0.24643374979496002 ]
    }
  }
}

尽管weight的两个部分都具有相同的形状和类型,但类型提供程序为我提供了两种不同但等效的类型:

type Weight =
    inherit IJsonDocument
    new : shape: int [] * data: float [] -> Weight
    member Data : float []
    member JsonValue: JsonValue
    member Shape: int []

type Weight2 =
    inherit IJsonDocument
    new : shape: int [] * data: float [] -> Weight2
    member Data : float []
    member JsonValue: JsonValue
    member Shape: int []

首先,这不太好,但是也许无法弄清楚它们的含义相同。因此,我坐下来尝试编写一个将两者结合在一起的函数,以便我可以从那里继续—我失败了。

我的第一个方法是使用重载:

type Tensor = {
    Data:single[]
    Shape:int list
} with
    static member Unify1 (w:NN.Weight) = { Data = w.Data |> Array.map single; Shape = w.Shape |> Array.toList }
    static member Unify1 (w:NN.Weight2) = { Data = w.Data |> Array.map single; Shape = w.Shape |> Array.toList }
  

错误FS0438 方法重复。一旦删除元组,函数,度量单位和/或提供的类型,方法Unify1与类型Tensor中的另一种方法具有相同的名称和签名。

然后我尝试了像这样的手动类型测试:

let unify2 (o:obj) =
    match o with
    | :? NN.Weight as w -> { Data = w.Data |> Array.map single; Shape = w.Shape |> Array.toList }
    | :? NN.Weight2 as w -> { Data = w.Data |> Array.map single; Shape = w.Shape |> Array.toList }
    | _ -> failwith "pattern oops"

此变体之所以无法编译,是因为

  

错误FS3062 不允许使用提供的类型JsonProvider<...>.Weight进行此类型测试,因为此提供的类型将在运行时被擦除为Runtime.BaseTypes.IJsonDocument

如何获取类型提供者以生成统一类型?另外,我如何在使编译器满意的同时自己统一它们?

1 个答案:

答案 0 :(得分:1)

我同意,推断的类型并不尽如人意。 XML type provider有一个静态参数Global,该参数根据名称在整个文档中统一XML元素-因此,JSON提供程序可以做类似的事情(但是要棘手,因为我们必须仅根据它们的字段或在父元素中使用的标签来识别两个记录是“相同的”。)如果您有兴趣为F#数据做贡献,请打开一个问题进行讨论!

同时,我认为一种合理的解决方法是获取基础JsonValue,然后将其包装回提供的Weight类型中。这将适用于两个weight记录,因为它们具有相同的字段:

type NN = JsonProvider<"""{ ... }""">

let processWeight (js:JsonValue) = 
  let w = NN.Weight(js)
  w.Data, w.Shape

let nn = NN.GetSample()
nn.Conv1.Weight.JsonValue |> processWeight
nn.Bn1.Weight.JsonValue |> processWeight

一个更好的版本是使用静态成员约束来访问JsonValue函数中的processWeight属性(以便您可以只调用nn.Conv1.Weight |> processWeight),但是我不想使得样品太复杂。