如何在F#类型提供程序中构建任意curried函数?
为了让类型提供程序生成更多的惯用代码,我开始研究从提供程序返回curried函数 下面是一段代码:如何在F#类型提供程序中构建任意curried函数?,f#,currying,type-providers,F#,Currying,Type Providers,为了让类型提供程序生成更多的惯用代码,我开始研究从提供程序返回curried函数 下面是一段代码: let lambdaTest () = let inner = <@ fun myInt -> fun int2 -> sprintf "A string! %d %d" myInt int2 @> let innerType = inner.GetType() ProvidedProperty(
let lambdaTest () =
let inner =
<@ fun myInt ->
fun int2 -> sprintf "A string! %d %d" myInt int2 @>
let innerType = inner.GetType()
ProvidedProperty(
"lambda",
innerType.GenericTypeArguments.[0],
IsStatic = true,
GetterCode = fun _ -> inner.Raw)
不幸的是,由于报价的返回类型相互冲突,上述F#代码无效
有人知道有没有办法做到这一点吗?我认为使用动态类型构建curried函数(取决于某些输入)可能是一种情况,您必须使用各种
Expr
构造函数手工构建报价
我想这可能是一个与你的问题有关的问题,所以我用它作为灵感。以下函数获取格式说明符列表args
,其中可以包含字符串的“s”
,或整数的“n”
。例如,给定['s';'n']
,它构建一个函数string->(int->string)
,该函数格式化前两个参数并返回一个包含结果的串联字符串:
open Microsoft.FSharp.Quotations
let rec buildFunc args printers =
match args with
| 's'::args ->
// Build a function `string -> (...)` where the `(...)` part is function
// or value generated recursively based on the remaining `args`.
let v = Var("v", typeof<string>)
let printer = <@@ "Str: " + (%%(Expr.Var v)) + "\n" @@>
// As we go, we accumulate a list of "printers" which are expressions of
// type `string` that return the variables we are building, formatted...
Expr.Lambda(v, buildFunc args (printer::printers))
| 'n'::args ->
// Pretty much the same, but we use `string<int>` to convert int to string
let v = Var("v", typeof<int>)
let printer = <@@ "Num: " + (string<int> (%%(Expr.Var v))) + "\n" @@>
Expr.Lambda(v, buildFunc args (printer::printers))
| [] ->
// Builds: String.Format [| f1; f2; f3 |] where 'f_i' are the formatters
let arr = Expr.NewArray(typeof<string>, List.rev printers)
let conc = typeof<string>.GetMethod("Concat", [|typeof<string[]>|])
Expr.Call(conc, [arr])
感谢@Tomas,这确实是受到printftoy提供程序的启发,但这也是一个更一般的问题,因为目前很难通过类型提供程序创建习惯的F#api。我将尝试尽快试用您的代码(或者可能在明天的会议上将其作为扩展问题提供给大家!)人们可以在以下位置查看此代码在上下文中的使用:。再次感谢你,托马斯!
open Microsoft.FSharp.Quotations
let rec buildFunc args printers =
match args with
| 's'::args ->
// Build a function `string -> (...)` where the `(...)` part is function
// or value generated recursively based on the remaining `args`.
let v = Var("v", typeof<string>)
let printer = <@@ "Str: " + (%%(Expr.Var v)) + "\n" @@>
// As we go, we accumulate a list of "printers" which are expressions of
// type `string` that return the variables we are building, formatted...
Expr.Lambda(v, buildFunc args (printer::printers))
| 'n'::args ->
// Pretty much the same, but we use `string<int>` to convert int to string
let v = Var("v", typeof<int>)
let printer = <@@ "Num: " + (string<int> (%%(Expr.Var v))) + "\n" @@>
Expr.Lambda(v, buildFunc args (printer::printers))
| [] ->
// Builds: String.Format [| f1; f2; f3 |] where 'f_i' are the formatters
let arr = Expr.NewArray(typeof<string>, List.rev printers)
let conc = typeof<string>.GetMethod("Concat", [|typeof<string[]>|])
Expr.Call(conc, [arr])
open Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter
// Generate 'int -> (string -> (int -> string))'
let fe = buildFunc (List.ofSeq "nsn") []
fe.Type.FullName // Shows the right type in a bit ugly way
// Evaluate the expression & cast to a function type
let f = (EvaluateQuotation(fe) :?> (int -> (string -> (int -> string))))
f 1 "a" 2 // Works!