I'd like to generate a type with static functions, that I can then use in functions that become quotations using the ReflectedDefinition attribute. It seems to me a convenient way of translating something into FSharp types, using FSharp to compose the domain of functions and types, and spit the composed code back into its native form, having received the benefit of type checking, VS intellisense, higher order functions, etc. To try and get started I have this type provider, largely copy->pasted from various articles
[<TypeProvider>]
type CSoundTypeProvider(config: TypeProviderConfig) as this =
inherit ProvidedTypes.TypeProviderForNamespaces()
let namespaceName = "TestNamespace"
let thisAssembly = Assembly.GetExecutingAssembly()
// config.
let intType = typeof<int>
let providedParam = ProvidedTypes.ProvidedParameter("prm", intType)
let providedFunction = ProvidedTypes.ProvidedMethod("TestMethod", [providedParam], intType, IsStaticMethod=true
, InvokeCode = fun args ->
// The 'args' parameter represents expressions that give us access to the
// instance on which the method is invoked and other parameters (if there are more)
let instance = args.[0]
// Now we can return quotation representing a call to MethodInfo 'p' with 'instance'
instance)
let csoundProvidedWrapper = ProvidedTypes.ProvidedTypeDefinition(thisAssembly, namespaceName, "TestType", None)
do csoundProvidedWrapper.AddMember(providedFunction)
do this.AddNamespace(namespaceName, [csoundProvidedWrapper])
And testing it with this reflected definition:
[<ReflectedDefinition>]
let myfn i j =
let k = i * j
let x = k + 2
let f = TestNamespace.TestType.TestMethod k
let ret = f + 2
ret
I'm parsing the reflected definition like this:
<@ myfn @> |> println
println is a function (copied from another article) that has many of the active patterns for parsing quotations like Patterns.Call(None, DerivedPatterns.MethodWithReflectedDefinition(n), expList), which gets me an experssion tree of all of the code, except for the provided static method. Is what I'm trying to do even possible? If so, what's the active pattern I've likely missed from my println function here:
let println expr =
let rec print expr = match expr with
| Patterns.Application(expr1, expr2) ->
// Function application.
print expr1
printf " "
print expr2
| Patterns.Call(None, DerivedPatterns.MethodWithReflectedDefinition(n), expList) ->
print n
| Patterns.Call(exprOpt, methodInfo, exprList) ->
// Method or module function call.
match exprOpt with
| Some expr -> print expr
| None -> printf "%s" methodInfo.DeclaringType.Name
printf ".%s(" methodInfo.Name
if (exprList.IsEmpty) then printf ")" else
print exprList.Head
for expr in exprList.Tail do
printf ","
print expr
printf ")"
| DerivedPatterns.Int32(n) ->
printf "%d" n
| Patterns.Lambda(param, body) ->
// Lambda expression.
printf "fun (%s:%s) -> " param.Name (param.Type.ToString())
print body
| Patterns.Let(var, expr1, expr2) ->
// Let binding.
if (var.IsMutable) then
printf "let mutable %s = " var.Name
else
printf "let %s = " var.Name
print expr1
printf " in "
print expr2
| Patterns.PropertyGet(_, propOrValInfo, _) ->
printf "%s" propOrValInfo.Name
| DerivedPatterns.String(str) ->
printf "%s" str
| Patterns.Value(value, typ) ->
printf "%s" (value.ToString())
| Patterns.Var(var) ->
printf "%s" var.Name
| _ -> printf "%s" (expr.ToString())
print expr
If I can't do this, what approach would you recommend for generating FSharp definitions that I can use in quotations? I've largely been influenced by the FunScript project, but hoping to avoid the step where it seems each Typescript definition has to get compiled into a seperate DLL.
Most of the type provider demos out there use erased type providers which do not produce actual .NET types. When you use an erased type provider, the generated methods are erased and replaced with the code you provided in the method's InvokeCode
.
Let's say you have a method Foo
that erases to someFunc
with "Foo"
as an argument:
myObj.Foo() ~> someFunc(myObj, "Foo")
In quotations, you will also see the erased version (both in direct <@ .. @>
and in ReflectedDefinition
):
<@ myObj.Foo() @> ~> <@ someFunc(myObj, "Foo") @>
The way FunScript type provider works is that it generates some dummy code that contains the function name so that it can then generate the corresponding JavaScript. Say you have:
<@ win.Alert(arg) @> ~> <@ invokeFunction("alert", win, arg) @>
To do the same sort of thing, you'll need to define a function like invokeFunction
and to generate appropriate InvokeCode
. Then you can look for calls to invokeFunction
in the generated quotation and do whatever special thing you need to do there. It is a bit hard to see what exactly are you trying to do, but this should at least point you in the right direction.