Technical Notes
A type provider is simultaneously a tool and a library. There is a component that runs at compile-time (also called "design-time") and a component that runs at runtime. F# type providers are hosted by applications using FSharp.Compiler.Service.
First, some terminology:
The Type Provider Run Time Component (TPRTC) is the component referenced by
#ror-r:on the command line or other configuration of a host tool, e.g.FSharp.Data.dll.The Type Provider Design Time Component (TPDTC), e.g.
FSharp.Data.DesignTime.dllis the DLL that gets loaded into host tools.The host tool is, for example
fsc.exeorfsi.exe, or some tool hostingFSharp.Compiler.Service.dllsuch asdevenv.exeorFsAutoComplete.exe.
The Type Provider Runtime Component (TPRTC)
This contains either a TypeProviderAssembly attribute indicating that this component is also a TPDTC, or TypeProviderAssembly("MyDesignTime.dll") attribute indicating that the name of the design time component.
TPRTCs are normally netstandard2.0 or above.
The Type Provider Design Time Component (TPDTC)
The Type Provider Design Time Component (TPDTC) is, for example, FSharp.Data.DesignTime.dll.
This is the DLL that gets loaded into host tools, and may be the same physical file as the TPRTC. This component includes the ProvidedTypes.fs/fsi files from the type provider SDK.
TPDTC are generally netstandard2.0 or netstandard2.1 components.
See Loading type providers for the rules to find TPDTC components.
Naming Conventions
The following guidance extends https://fsharp.github.io/2014/09/19/fsharp-libraries.html.
A type provider for a data source or schema format XYZ can often be placed in
FSharp.Data, e.g. “FSharp.Data.XYZ”.A type provider for interoperability can often be placed in
FSharp.Interop, e.g. “FSharp.Interop.XYZ”.
Good type provider naming examples:
FSharp.Text.RegexProviderFSharp.Azure.StorageTypeProvider
Here are some examples of existing type providers that aren't too bad (they are clear) but could be renamed to follow the guidelines:
ExcelProvider(better would beFSharp.Interop.ExcelProvider)RProvider(better would beFSharp.Interop.RProvider)ApiaryProvider(better would beFSharp.Data.ApiaryProvider)SQLProvider(better would beFSharp.Data.SQLProvider)DynamicsNAVProvider(better would beFSharp.Interop.DynamicsNAVProvider)DynamicsCRMProvider(better would beFSharp.Interop.DynamicsCRMProvider)
Lifetime of type provider instantiations
F# type providers are hosted by applications using FSharp.Compiler.Service. These notes describe the lifetime and typical resource usage of type provider instances for applications that incorporate FSharp.Compiler.Service (the host).
Each time the host application (e.g. devenv.exe) checks a file using type providers (e.g. containing JsonProvider<"...">), one or more new TP instantiations may be created, along with subsequent calls to ApplyStaticArguments.
- The F# compiler service doesn't try to cache these (except where it caches the TAST structures that results of checking a file or project).
- Individual type providers may use caching of some kind, returning previously provided types when the type provider is instantiated the same way. Care should be taken that these caches do not permanently occupy resources
- Under the hood, the majority of resources used by a TP instantiation are those required to "map" the generated types to the referenced assemblies. To support this, each TP Instantiation creates one ILModuleReader for each referenced assembly. When the compiler is used as a service, the natural (minimal) lifetime of an ILModuleReader is the same as its TP Instantiation. The TPSDK may share these resources.
- The natural (i.e. minimal) lifetime of a TP Instantiation and its related objects (ProvidedType ProvidedMethodInfo etc. etc. ) is the same as the TAST structures which refer to these things (TProvidedTypeInfo, MethInfo, FieldInfo from infos.fs).
The lifetime of TAST structures is as long as they are held in the IncrementalBuilder, or you hold on to FSharpCheckFileResults, or FSharpCheckProjectResults, or FSharpAssemblyContents.
Explicit construction of code: MakeGenericType, MakeGenericMethod and UncheckedQuotations
Some type providers need to build code via explicit calls to FSharp.Quotations.Expr.* rather than via quotation
literals. Frequently, this is needed when code must instantiate generic methods or types. However, in some cases limitations
of the F# quotations API are reached.
In these cases, follow these rules
- Always use
ProvidedTypeBuilder.MakeGenericType(type, typeArguments)rather thantype.MakeGenericType(typeArguments) - Always use
ProvidedTypeBuilder.MakeGenericMethod(methInfo, methTypeArguments)rather thanmethInfo.MakeGenericType(methTypeArguments) - Where necessary open
open ProviderImplementation.ProvidedTypes.UncheckedQuotationsand make quotation nodes representing calls and other operations usingExpr.CallUnchecked.
If you don't do this you may get errors like
|
or
|
FSharp.TypeProviders.SDK