fp | FP‎ > ‎fp | F#‎ > ‎f# | Quotation‎ > ‎

f#.@@ | Compile Quotation to Assembly


I would like to know if there is a way to compile a code quotation into an assembly?

I understand that it is possible to call CompileUntyped() or Compile() on the Exp<_> object, for example:

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

However, how can I persist com to disk as an assembly?


asked Aug 6 '11 at 13:39
I am not sure if PowerPack supports this. But as an aside, I would not advise using PowerPack at all. Their code is often buggy or too slow. Writing your own compiler or evaluator from scratch using System.Reflection.Emit would probably give better results. There is also the problem that F# does not optimize the quotations, for example matching becomes a series of if/then/else, instead of the jump instruction it is in CIL in normal F# compilation. –  toyvo Aug 6 '11 at 16:25
Hi, thanks for this, I guess I will look into alternatives them (such as Reflection.Emit as you mentionned). – Ncc Aug 7 '11 at 20:28
possible duplicate of F# equivalent to Eval –  mydogisbox Mar 19 at 18:09
add comment

You can evaluate an F# Code Quotations with pattern matching:

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 @>

See the F# PowerPackUnquoteFoq OSS projects or this snippet for more complete implementations.

To compile an F# Code Quotation you can define a dynamic method using 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
    fun () -> f.Invoke(null,[||]) :?> int

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

To compile to an assembly again use Reflection.Emit to generate a type with a method:

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

createAssembly <@ 1 + 1 @>

See the Fil project (F# to IL) for a more complete implementation.

answered Apr 9 at 7:27
+1 for starting a new project that does this! –  Govert Apr 14 at 18:38
add comment