Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# - Generate simple empty type from string at compile time

i was wondering if it's possible to generate a something similar to a simple type provider (Record or Union, with no members, just the matching name with the string name) from a string, at compile time.

mixing this

http://www.readcopyupdate.com/blog/2014/09/18/faking-typeclasses-using-static-type-constraints.html

and this

Create Discriminated Union Case from String

or a similar approach to record types.

what i would like to obtain in, for example (not necessarily with the same approach):

[<Literal>]
let myTypeName = "One"

type SingleStringTypeProvider = ... (here implementation)


type Provided = SingleStringTypeProvider<singleString>


let typeName = typeof<Provided.One>.Name

final result: One is a Type at compile time (and not a method nor a function)

EDIT

As suggested in the first answer (thanks a lot : )), I have tried to implement it with type provided, but i am still struggling when trying to access the type provider from my script file, apparently i see only the created type but non the type provider itself?

module SimpleStringProvider

open ProviderImplementation.ProvidedTypes
open Microsoft.FSharp.Core.CompilerServices

[<TypeProvider>]
type SingleStringTypeProvider (config : TypeProviderConfig) as this =
    inherit TypeProviderForNamespaces (config)

    let asm = System.Reflection.Assembly.GetExecutingAssembly()
    let ns = "SimpleStringProvider"
    let stringProvider = ProvidedTypeDefinition(asm, ns, "SingleStringTypeProvider", Some(typeof<obj>))

    // Define one static parameter with type name
    let parameter = ProvidedStaticParameter("TypeName", typeof<string>)
    do stringProvider.DefineStaticParameters([parameter], fun typeName args ->
    // Create the main type (this corresponds to `Provided`)    
    let resTy = ProvidedTypeDefinition(asm, ns, typeName, Some(typeof<obj>))

    // Add a nested type as a member using the name from the parameter
    let typeName = args.[0] :?> string
    ProvidedTypeDefinition(typeName, None)
    |> resTy.AddMember

    resTy )


[<assembly:TypeProviderAssembly>]
do ()

and here is the code in my script.fsx file, probably i am making some silly mistakes i guess.

#r @".\testType\SimpleStringProvider.dll"

open SimpleStringProvider

type x = SimpleStringProvider.SingleStringTypeProvider<"test">

ERROR in script.fsx file

The non-generic type 'SimpleStringProvider.SingleStringTypeProvider' does not expect any type arguments, but here is given 1 type argument(s)

like image 616
jkone27 Avatar asked Apr 08 '26 14:04

jkone27


1 Answers

You can provide nested types and those can be based on the static parameter. In your example Provided is a type and Provided.One can be a nested type.

To do this, you can write something like this:

[<TypeProvider>]
type public SingleStringTypeProvider(cfg:TypeProviderConfig) as this =
  inherit TypeProviderForNamespaces()

  // Generate namespace and the main type provider
  let asm = System.Reflection.Assembly.GetExecutingAssembly()
  let ns = "Samples"
  let stringProvider = ProvidedTypeDefinition(asm, ns, "SingleStringTypeProvider", Some(typeof<obj>))

  // Define one static parameter with type name
  let parameter = ProvidedStaticParameter("TypeName", typeof<string>)
  do stringProvider.DefineStaticParameters([parameter], fun typeName args ->
    // Create the main type (this corresponds to `Provided`)    
    let resTy = ProvidedTypeDefinition(asm, ns, typeName, Some typeof<IniFile>)

    // Add a nested type as a member using the name from the parameter
    let typeName = args.[0] :?> string
    ProvidedTypeDefinition(typeName, None)
    |> resTy.AddMember

    resTy )

[<assembly:TypeProviderAssembly>]
do()

I have not tested this, so you might need to do some tweaking, but I think it should work.

like image 110
Tomas Petricek Avatar answered Apr 11 '26 06:04

Tomas Petricek



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!