user963935
user963935

Reputation: 652

Pass "generic" JsonProvider to function in F#

I have two JsonProvider types:

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

I'd like to have a function, that can accept any of these and parse json using provided JsonProvider, something like this:(pseudo code, not compiling)

let parseUsingSpecificProvider json (provider : JsonProvider<GENERIC>) =
    provider.Parse json

Upvotes: 1

Views: 382

Answers (2)

CaringDev
CaringDev

Reputation: 8551

In functional programming it is common to replace 'single function interfaces' with the function itself. I.e. instead of passing in a parser having a Parse method, just use the Parse function as a first class value:

#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 }"""

If you need several methods of the provided type, you could use e.g. a record containing all necessary data:

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 }"""

While SRTPs are powerful (and cool), their usage should be limited. Overuse slows down the compiler and makes code harder to understand.

Upvotes: 0

Gus
Gus

Reputation: 26204

You can define an inline function with Statically Resolved Type Parameters (SRTPs):

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>

So it will work with any type that has the static method Parse accepting a string as input.

Upvotes: 2

Related Questions