是否可以将类型提供程序作为参数传递给函数 [英] Can a Type Provider be passed into a function as a parameter

查看:72
本文介绍了是否可以将类型提供程序作为参数传递给函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习F#和 FSharp.Data库.我有一项任务,需要读取20个CSV文件.每个文件具有不同的列数,但记录具有相同的性质:键入日期字符串,其余所有列均为浮点数.在将结果持久保存到数据库之前,我需要对浮点格式数据列进行一些统计计算.尽管我已完成所有管道逻辑工作:

  • 通过FSharp.Data CSV类型提供程序读取CSV
  • 使用反射来获取每个列字段的类型,以及将它们输入到模式匹配中的标题名称,这决定了相关的计算逻辑
  • sqlbulkcopy结果),我结束了20个函数(每个CSV文件1个).

该解决方案远远不能接受.我以为可以创建通用的顶层函数作为驱动程序来遍历所有文件.但是,经过数天的尝试,我一无所获.

FSharp.Data CSV类型提供程序具有以下模式:

type Stocks = CsvProvider<"../docs/MSFT.csv">
let msft = Stocks.Load("http://ichart.finance.yahoo.com/table.csv?s=MSFT")
msft.Data |> Seq.map(fun row -> do something with row)
...

我尝试过:

let mainfunc (typefile:string) (datafile:string) =
    let msft = CsvProvider<typefile>.Load(datafile)
    ....

这不起作用,因为CsvProvider抱怨类型文件不是有效的常量表达式.我猜想类型提供者必须在编码时需要文件来推断列的类型,类型推断不能推迟到使用相关信息调用mainfunc的代码之前.

然后我尝试将Type作为参数传递给mainfunc

都不是

let mainfunc (typeProvider:CsvProvider<"../docs/MSFT.csv">) =
    ....

也不

let mainfunc<typeProvider:CsvProvider<"../docs/MSFT.csv">> =
    ....

工作.

然后我尝试通过

传递MSFT

type Stocks = CsvProvider<"../docs/MSFT.csv">
let msft = Stocks.Load("http://ichart.finance.yahoo.com/table.csv?s=MSFT")

进入mainFunc.根据智能,MSFT的类型为CsvProvider<...>,而MSFT的数据类型为seq<CsvProvider<...>>.我试图用这两个的显式类型声明一个输入参数,但是它们都不能通过编译.

任何人都可以帮助您,并向我指出正确的方向吗?我在这里缺少基本的东西吗?可以在F#函数中使用任何.net类型和类对象来显式指定参数类型,但是我可以对类型提供程序中的类型进行相同的操作吗?

如果对上述问题的回答为否",那么使逻辑通用以处理20个文件甚至200个不同文件的替代方法是什么?

解决方案

这与 http://bluemountaincapital.github.io/Deedle/)

I am learning F# and the FSharp.Data library. I have a task which I need to read 20 CSV files. Each file has different number of columns but the records share the same nature: keyed on a date string and all the rest of the columns are float numbers. I need to do some statistical calculation on the float format data columns before persist the results into the database. Although I got all the plumbing logic working:

  • read in the CSV via FSharp.Data CSV type provider,
  • use reflection to get the type of the each column fields together with the header names they are fed into a pattern match, which decides the relevant calculation logics
  • sqlbulkcopy the result), I ended 20 functions (1 per CSV file).

The solution is far from acceptable. I thought I could create a generic top level function as the driver to loop through all the files. However after days of attempts I am getting nowhere.

The FSharp.Data CSV type provider has the following pattern:

type Stocks = CsvProvider<"../docs/MSFT.csv">
let msft = Stocks.Load("http://ichart.finance.yahoo.com/table.csv?s=MSFT")
msft.Data |> Seq.map(fun row -> do something with row)
...

I have tried:

let mainfunc (typefile:string) (datafile:string) =
    let msft = CsvProvider<typefile>.Load(datafile)
    ....

This doesnt work as the CsvProvider complains the typefile is not a valid constant expression. I am guessing the type provider must need the file to deduce the type of the columns at the coding time, the type inference can not be deferred until the code where the mainfunc is called with the relevant information.

I then tried to pass the Type into the mainfunc as a parameter

neither

let mainfunc (typeProvider:CsvProvider<"../docs/MSFT.csv">) =
    ....

nor

let mainfunc<typeProvider:CsvProvider<"../docs/MSFT.csv">> =
    ....

worked.

I then tried to pass the MSFT from

type Stocks = CsvProvider<"../docs/MSFT.csv">
let msft = Stocks.Load("http://ichart.finance.yahoo.com/table.csv?s=MSFT")

Into a mainFunc. According to the intellisence, MSFT has a type of CsvProvider<...> and MSFT.Data has a type of seq<CsvProvider<...>>. I have tried to declare a input parameter with explicit type of these two but neither of them can pass compile.

Can anyone please help and point me to the right direction? Am I missing somthing fundamental here? Any .net type and class object can be used in a F# function to explicitly specify the parameter type, but can i do the same with the type from a type provider?

If the answer to above question is no, what are the alternative to make the logic generic to handle 20 files or even 200 different files?

解决方案

This is related to Type annotation for using a F# TypeProvider type e.g. FSharp.Data.JsonProvider<...>.DomainTypes.Url

Even though intellisense shows you CsvProvider<...>, to reference the msft type in a type annotation you have to use Stocks, and for msft.Data, instead of CsvProvider<...>.Row, you have to use Stocks.Row.

If you want to do something dynamic, you can get the columns names with msft.Headers and you can get the types of the columns using Microsoft.FSharp.Reflection.FSharpType.GetTupleElements(typeof<Stocks.Row>) (this works because the row is erased to a tuple at runtime)

EDIT:

If the formats are incompatible, and you're dealing with dynamic data that doesn't conform to a common format, you might want to use CsvFile instead (http://fsharp.github.io/FSharp.Data/library/CsvFile.html), but you'll lose all the type safety of the type provider. You might also consider using Deedle instead (http://bluemountaincapital.github.io/Deedle/)

这篇关于是否可以将类型提供程序作为参数传递给函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆