I think that building curried functions with dynamic type (depending on some input) is probably a case where you have to resort to building the quotation by hand using the various Expr
constructors.
I guess this might be a question related to your printf type provider, so I used that as an inspiration. The following function takes a list of format specifiers args
which can contain either "s"
for strings or "n"
for integers. Given e.g. ['s'; 'n']
, it builds a function string -> (int -> string)
that formats the first two arguments and returns a concatenated string with the results:
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])
I have not tried this in the type provider context, but it can be compiled and evaluated:
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!