2013-06-03 2 views
3

Я использую FSharp.Data typeproviders.Как передать статический параметр в typeprovider в f # внутри функции

Я хотел бы сделать функцию, которая имеет параметр, который устанавливает строку или местоположение файла typeprovider's.

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

Если вызов JsonProvider в модуле с

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

type provider = JsonProvider<sample> 

работает отлично. Почему я не могу передать его как parameter? Я знаю, что это связано с тем, что эта ссылка ясна во время компиляции, но не может понять, как обойти ее, кроме объявления каждого providers explicitly.

ответ

4

Функция не может принимать значение статического параметра в качестве аргумента, потому что значение должно быть определено во время компиляции. Это означает, что если вы пишете:

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

... все нормально, потому что компилятор знает, что sample является постоянным (и компилятор знает его значение), и поэтому он может создать экземпляр поставщика типа (во время компиляции) для генерации типов. Если вы пишете что-то вроде:

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

... то компилятор не может знать, что значение sample может быть во время выполнения и поэтому он не может генерировать необходимые типы во время компиляции. Поставщики типов не «существуют» во время выполнения, поэтому типы не могут генерироваться «на лету» (это было бы не очень полезно, поскольку провайдеры типа типа должны предоставить вам некоторые гарантии безопасности во время компиляции).

Ваш пример. В вашем случае, это может иметь смысл взять конкретную JsonProvider<sample>.Parse функцию в качестве аргумента вместо:

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

Таким образом, абонент может указать статический параметр к провайдеру типа, а затем позвонить в вашу функцию, чтобы сделать синхронизацию :

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

Хотя, не совсем понятно, почему вам нужен провайдер типа здесь. Пункт провайдеров - дать вам хорошие типы, которые вы можете использовать для доступа к конкретному источнику данных. Если ваши converter и storer работают на любом файле данных JSON, то вы могли бы сделать это, используя только JSON parser (also in F# Data).

Асинхронный блок. Кроме того, обратите внимание, что ваш код на самом деле не работает асинхронно - сделать это асинхронно, вам нужно загрузить URL с помощью let! операции:

let SyncIt url parser storer = 
    async { 
     let wc = new WebClient() 
     let! html = wc.AsyncDownloadString(url) 
     parser html 
     |> Seq.iter (converter >> storer) 
    } 
+0

Это имеет смысл, спасибо! – Remko

+0

Я добавил несколько подробностей. Я не совсем понял, почему вам нужно использовать поставщиков типов, если ваши «конвертер» и «хранитель» могут (по-видимому) работать на любом JSON - поэтому они не пользуются статически известными типами. –

+0

Я нахожусь на своей второй неделе от f # и начать разбираться с некоторыми концепциями. Я использую typeprovider, потому что мне нравится, что большинство типов значений были распознаны. Конвертер отображает из DomainTypes.Entity тип записи, который я определил. Эта запись хранится в Redis.При попытке написать конвертер мне не удастся получить более одного возвращаемого типа. Спасибо за объяснение. Регс, Ремко – Remko