How to create new type at runtime in F#? [closed]

2020-08-09 04:46发布

问题:

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 7 years ago.

Please give an example of how to create new type (say, two types Cartesian product) in F# at runtime with reflection?

UPDATE

I am looking for a language with first class types. I was told F# can this. I tried nothing since didn't learned F# yet. I just want to see how it's made.

回答1:

The following F# code takes 2 sequences of values (rank and suit in the example) and returns the cartesian product as a sequence of pairs (cards), using a pair type dynamically generated at runtime using Reflection:

open System
open System.Reflection
open System.Reflection.Emit
open Microsoft.FSharp.Reflection

/// Creates a dynamic module via reflection
let createModule () =
    let name = Guid.NewGuid().ToString()
    let d = AppDomain.CurrentDomain
    let a = d.DefineDynamicAssembly(AssemblyName(name), AssemblyBuilderAccess.Run)
    a.DefineDynamicModule(name)
/// Creates a dynamic pair type using the specified x and y types
let createPairType (x:Type, y:Type) =
    let m = createModule()
    let t = m.DefineType("Pair", TypeAttributes.Public ||| TypeAttributes.Class)
    let x = t.DefineField(x.Name, x, FieldAttributes.Public)
    let y = t.DefineField(y.Name, y, FieldAttributes.Public)
    t.CreateType()
/// Creates a pair value using the specified pair type
let createPairValue (pairType:Type) (x:'X, y:'Y) =
    let instance = Activator.CreateInstance(pairType)
    pairType.GetField(typeof<'X>.Name).SetValue(instance, x)
    pairType.GetField(typeof<'Y>.Name).SetValue(instance, y)
    instance
/// Creates a cartesian product 
let createCartesianProduct (xs:'X seq, ys:'Y seq) =
    let pairType = createPairType (typeof<'X>,typeof<'Y>) 
    seq { for x in xs do for y in ys -> createPairValue pairType (x, y) }
/// Defines dynamic lookup operator for accessing a named field
let inline (?) (x:obj) name = x.GetType().GetField(name).GetValue(x)
/// Card suit discriminated union type
type Suit = Club | Diamond | Heart | Spade
/// Card rank discriminated union type 
type Rank = | One | Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten
            | Jack | Queen | King | Ace
/// Gets union case values
let getUnionValues<'T>() = 
    FSharpType.GetUnionCases(typeof<'T>) 
    |> Seq.map (fun x -> FSharpValue.MakeUnion(x,[||]) :?> 'T)
let ranks, suits = getUnionValues<Rank>(), getUnionValues<Suit>()
/// Sequence of dynamically generated pairs
let cards = createCartesianProduct (ranks, suits)
// Paste this into F# interactive to print the generated cards
for card in cards do printfn "%A %A" card?Rank card?Suit


回答2:

Taken from my (non-free) article Structural Typing in the F#.NET Journal:

The following createType function creates a new .NET assembly, new module and new public class type of the given name:

> let createType typeName =
    let name = System.Reflection.AssemblyName(Name="tmpAssembly")
    let run = System.Reflection.Emit.AssemblyBuilderAccess.Run
    let builder = System.Threading.Thread.GetDomain().DefineDynamicAssembly(name, run)
    let mdl = builder.DefineDynamicModule "tmpModule"
    let attrs = TypeAttributes.Public ||| TypeAttributes.Class
    mdl.DefineType(typeName, attrs);;
val createType : string -> TypeBuilder