通过“通用”JsonProvider在F#中运行

时间:2017-12-06 09:05:50

标签: f# type-providers

我有两种JsonProvider类型:

CROSS JOIN UNNEST

我想要一个函数,它可以接受任何这些并使用提供的JsonProvider解析json,如下所示:(伪代码,不编译)

type Provider1 = JsonProvider<"""{structure}""">
type Provider2 = JsonProvider<"""{}structure2""">

2 个答案:

答案 0 :(得分:2)

您可以使用静态分辨类型参数(SRTP)定义内联函数:

let inline parseUsingSpecificProvider json (provider:^T) =
    (^T : (static member Parse : string -> _) json) 

// tests
#r @"packages\FSharp.Data\lib\net40\FSharp.Data.dll"
open FSharp.Data

type Provider1 = JsonProvider<""" { "name":"John"           } """>
type Provider2 = JsonProvider<""" { "name":"John", "age":94 } """>

let x = parseUsingSpecificProvider  (""" { "name":"Tomas", "age":4 } """) Unchecked.defaultof<Provider1>
let y = parseUsingSpecificProvider  (""" { "name":"Tomas", "age":4 } """) Unchecked.defaultof<Provider2>

因此它适用于任何具有静态方法Parse接受字符串作为输入的类型。

答案 1 :(得分:0)

在函数式编程中,通常用函数本身替换“单个函数接口”。即而不是传递具有Parse方法的解析器,只需使用Parse函数作为第一类值:

#r @"packages\FSharp.Data\lib\net40\FSharp.Data.dll"
open FSharp.Data

type Provider1 = JsonProvider<""" { "name":"John"           } """>
type Provider2 = JsonProvider<""" { "name":"John", "age":94 } """>

let parse parseFn json =
    parseFn json

let p1 = parse Provider1.Parse """{ "name": "Not John" }"""
let p2 = parse Provider2.Parse """{ "name": "Not John either", "age": 12 }"""

如果您需要提供类型的多种方法,可以使用例如包含所有必要数据的记录:

type Parser<'b> = {
    Parse : string -> 'b
    GetSample : unit -> 'b
}

let parseUsing parser text =
    printfn "Parsing things like %A" <| parser.GetSample()
    parser.Parse text

let parser1 = { Parse = Provider1.Parse; GetSample = Provider1.GetSample }
let parser2 = { Parse = Provider2.Parse; GetSample = Provider2.GetSample }

let p1' = parseUsing parser1 """{ "name": "Not John" }"""
let p2' = parseUsing parser2 """{ "name": "Not John either", "age": 12 }"""

虽然SRTP功能强大(而且很酷),但它们的使用应该受到限制。过度使用会降低编译器的速度,使代码更难理解。