Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass a static parameter to a typeprovider in f# inside a function

I'm using the FSharp.Data typeproviders.

I would like to make a function that has a parameter that sets the typeprovider's sample string or file location.

let SyncIt url sample converter storer =
    async {
        url
        |> MakeRequestAsync 
        |> Async.RunSynchronously
        |> JsonProvider< sample >.Parse
        |> Seq.iter (converter >> storer)
    }

If a call the JsonProvider in the module with

[<Literal>]
let sample = """{"name":"Peter","age":9}"""

type provider = JsonProvider<sample>

works fine. Why can't I pass it as a parameter? I know it has something to do with the reference being clear at compile time, but cannot figure out how to get around it other than declaring each providers explicitly.

like image 826
Remko Avatar asked Feb 16 '23 08:02

Remko


1 Answers

A function cannot take a value of static parameter as an argument, because the value has to be determined at compile time. This means that if you write:

let [<Literal>] sample = """{"name":"Peter","age":9}"""
let parseHtml html = JsonProvider<sample>.Parse(html)

... everything is fine, because the compiler knows that sample is a constant (and the compiler knows its value) and so it can instantiate the type provider (during compilation) to generate types. If you write something like:

let parseHtml sample html = JsonProvider<sample>.Parse(html)

... then the compiler cannot know what the value of sample may be at runtime and so it cannot generate the required types at compile time. Type providers do not "exist" at runtime, so the types cannot be generated on the fly (This would not be really useful, because the point of type providers is to give you some compile-time safety guarantees).

Your example. In your case, it might make sense to take the specific JsonProvider<sample>.Parse function as an argument instead:

let SyncIt url parse storer =
    async {
        url
        |> MakeRequestAsync 
        |> Async.RunSynchronously
        |> parse
        |> Seq.iter (converter >> storer)
    }

This way, the caller can specify the static parameter to a type provider and then call your function to do the synchronization:

let [<Literal>] sample = """{"name":"Peter","age":9}"""
SyncIt url (JsonProvider<sample>.Parse) storer

Although, it is not entirely clear to me why you need a type provider here. The point of providers is to give you nice types that you can use to access a concrete source of data. If your converter and storer work on any JSON data file, then you might be able to get the thing done using just JSON parser (also in F# Data).

Asynchronous block. Also, note that your code is not really running asynchronously - to make it asynchronous, you need to download the URL using let! operation:

let SyncIt url parser storer =
    async {
        let wc = new WebClient() 
        let! html = wc.AsyncDownloadString(url)
        parser html
        |> Seq.iter (converter >> storer)
    }
like image 134
Tomas Petricek Avatar answered Apr 27 '23 10:04

Tomas Petricek