问题 F#如何将代码报价编译到程序集


我想知道是否有办法将代码报价编译成程序集?

我知道可以打电话 CompileUntyped() 要么 Compile() 在...上 Exp<_> 对象,例如:

let x = <@ 1 * 2 @>
let com = x.Compile()

但是,我怎么能坚持下去 com 磁盘作为程序集?

谢谢。


12758
2017-08-06 13:39


起源

我不确定PowerPack是否支持此功能。但是,除此之外,我根本不会建议使用PowerPack。他们的代码通常是错误或太慢。使用System.Reflection.Emit从头开始编写自己的编译器或赋值器可能会产生更好的结果。还有一个问题是F#没有优化引用,例如匹配变成一系列if / then / else,而不是正常F#编译中它在CIL中的跳转指令。 - t0yv0
嗨,谢谢你,我想我会研究替代品(比如你提到的Reflection.Emit)。 - Ncc
您可能会发现这很有用: stackoverflow.com/questions/2682475/... 和 stackoverflow.com/questions/1618682/... - JPW
可能重复 F#相当于Eval - mydogisbox


答案:


您可以使用模式匹配来评估F#代码报价:

open Microsoft.FSharp.Quotations.Patterns

let rec eval  = function
    | Value(v,t) -> v
    | Call(None,mi,args) -> mi.Invoke(null, evalAll args)
    | arg -> raise <| System.NotSupportedException(arg.ToString())
and evalAll args = [|for arg in args -> eval arg|]

let x = eval <@ 1 * 3 @>

F#PowerPack引文结束FOQ OSS项目或此 片段 更完整的实施。

要编译F#代码报价,您可以 定义动态方法 运用 Reflection.Emit的

open System.Reflection.Emit

let rec generate (il:ILGenerator) = function
    | Value(v,t) when t = typeof<int> ->
        il.Emit(OpCodes.Ldc_I4, v :?> int)
    | Call(None,mi,args) -> 
        generateAll il args
        il.EmitCall(OpCodes.Call, mi, null)
    | arg -> raise <| System.NotSupportedException(arg.ToString())
and generateAll il args = for arg in args do generate il arg

type Marker = interface end

let compile quotation =
    let f = DynamicMethod("f", typeof<int>, [||], typeof<Marker>.Module)
    let il = f.GetILGenerator()
    quotation |> generate il
    il.Emit(OpCodes.Ret)
    fun () -> f.Invoke(null,[||]) :?> int

let f = compile <@ 1 + 3 @>
let x = f ()

要再次编译到程序集,请使用Reflection.Emit生成带有方法的类型:

open System
open System.Reflection

let createAssembly quotation =
    let name = "GeneratedAssembly"
    let domain = AppDomain.CurrentDomain
    let assembly = domain.DefineDynamicAssembly(AssemblyName(name), AssemblyBuilderAccess.RunAndSave)
    let dm = assembly.DefineDynamicModule(name+".dll")
    let t = dm.DefineType("Type", TypeAttributes.Public ||| TypeAttributes.Class)
    let mb = t.DefineMethod("f", MethodAttributes.Public, typeof<int>, [||])
    let il = mb.GetILGenerator()
    quotation |> generate il
    il.Emit(OpCodes.Ret)
    assembly.Save("GeneratedAssembly.dll")

createAssembly <@ 1 + 1 @>

费尔 项目(F#到IL)以实现更完整的实施。


12
2018-04-09 07:27



+1用于启动执行此操作的新项目! - Govert
有执行 github.com/eiriktsarpalis/QuotationCompiler 避免了如上所述使用Reflection来编译Code Quotation的需要。这个实现似乎很大程度上使用了Microsoft.FSharp.Compiler命名空间,这可能以前无法访问... - Sam
@Sam好点Eirik的Quotation Compiler可能是现在的方法 - Phillip Trelford