How to reference 'generated' types created by a TypeProvider - f#

I am trying to implement a type provider for Excel files that creates generated types. My goal is to be able to reference these types from C#.
If I create the type provider using erased types, I can reference it from F# using this syntax:
type DataTypesTest = ExcelFile<"tests\ExcelProvider.Tests\DataTypes.xlsx">
If I mark my types as generated. The above syntax produces this error:
A direct reference to the generated type 'ExcelFile' is not permitted. Instead, use a type definition, e.g. 'type TypeAlias = <path>'. This indicates that a type provider adds generated types to your assembly.
How do I reference a parameterized generated type in my F# code? The source for this type provider is available on GitHub

I believe the problem may be due to how you're constructing the type in the Type Provider, not your code at the usage site. The code to use the type provider should be identical for generated or erased type providers. I had similar errors occur when trying to get the type construction correct with my generated type providers.
In your case, your type derives from ExcelFileInternal, so you need to use BaseConstructorCall, not just the constructor logic. This should look something similar to:
// add a parameterless constructor which loads the file that was used to define the schema
let ctor = ProvidedConstructor([])
ctor.BaseConstructorCall <- fun [] -> <## ExcelFileInternal(resolvedFilename, range) ##>
providedExcelFileType.AddMember(ctor)
Note that all constructors would need this type of change.

Related

Is the FSharp.Data XML type provider generative or not?

(previously "using type provider from C#")
This should be easy to answer, and the underlying issue has be asked before, but I'm slightly confused about it, having dug into it, there's something I'm missing.
The Xml type provider in
https://fsharp.github.io/FSharp.Data/library/XmlProvider.html
is erased?
so the types in it can't be used from another assembly?
"Erasing Type Providers produce types that can only be consumed in the assembly or project they are generated from" (says the MS docs).
but if I define in one assembly this
module Xml
type Xml = FSharp.Data.XmlProvider<"""
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>""">
let foo () = Xml.Parse ("")
and in another assembly, reference the above assembly and go...
let x = Xml.foo ()
let s = x.From
this compiles...so if the MS docs are correct, the provider must be generative?
if I implement the same code in C#,
var x = Xml.foo();
var y = x.From;
then this code doesnt compiler
'XmlElement' does not contain a definition for 'From' and no accessible extension method 'From' accepting a first argument of type 'XmlElement'
which seems to imply its erased...or I need to do something else (I've included the same dependencies).
My hunch is its erased, but then how does the F# assembly compile?
(I want it to be generative to use from C# and VB).
As far as I can tell by looking at the source code, all the types provided by FSharp.Data are erased.
The XmlProvider type is defined by this line at https://github.com/fsharp/FSharp.Data/blob/master/src/Xml/XmlProvider.fs#L26
let xmlProvTy = ProvidedTypeDefinition(asm, ns, "XmlProvider", None, hideObjectMethods=true, nonNullable=true)
As you can see, the instantiation of the ProvidedTypeDefinition type does not include isErased=false.
The ProvidedTypeDefinition type has two constructors. See the signatures at https://github.com/fsprojects/FSharp.TypeProviders.SDK/blob/master/src/ProvidedTypes.fsi#L268-L275
Both constructors have an implementation including this line, which means that the provide type is erased by default:
let isErased = defaultArg isErased true
Without redesigning the XmlProvider, the only way you can consume it from C# is to wrap it in an F# class that exposes C#-friendly types. Not sure if that would result in a better design than just avoiding the type provider altogether.
Assuming by "erased" you mean being removed from the FSharp.Data Library? then XMLProvider its probably not removed from it because I just tried your code and it works in F# project (as you have also noticed).
Now about the C# project the thing is TypeProviders are a Fsharp specific thing, whatever magic F# compiler is doing to type check xml and access the From xml-node here is probably only exclusive to F#.
The type of you variable x here is FSharp.Data.Runtime.BaseTypes.XmlElement, looking at the documentation here and the source code here.
There doesn't seem to be any way of accessing the xml nodes in an OOP way. Its probably not meant to be accessed that way either. If you want to parse xml and read its nodes in C# there are plenty of other ways for that. One being XmlReader in System.Xml

Get type of a module at compile time in F#

We know that in C# we can get type of a type at compile time using typeof(OutType) this lets us pass this to attributes later as it is constant expression.
I saw this question, but it doesn't really address the compile time usage.
So my question is: Is there a way to get System.Type of a given module at compile type within F# standard library?
F# disallows obtaining a module's type using its typeof operator by design, as they're not first-class concepts in the language.
From the spec, section 13.2:
F# modules are compiled to provide a corresponding compiled CLI type declaration and
System.Type object, although the System.Type object is not accessible by using the typeof
operator.
Modules compile to static classes however, so it is possible to obtain the type at runtime using reflection (and that's what happens in typeof<MyModule.Dummy>.DeclaringType example), and it's possible to obtain the type of a module defined in a referenced F# assembly using typeofoperator in C#.
For what you're trying to do, you'd best use a class instead of a module, because then you can get hold of the type without hassle:
type MyFactoryClass =
static member TestCases = [ 1; 2; 3 ]
...
[<Test; TestCaseSource(typeof<MyFactoryClass>, "TestCases">]
let test (arg: int) = ...
If you are OK to refer to a type inside that module (or create a dummy one if there are no types), you could do this:
module MyModule =
type Dummy = Dummy
let myModule = typeof<MyModule.Dummy>.DeclaringType

Creating Generic Types via Type Providers

Is it possible to create a generic type via type providers so that
[<TypeProvider>]
type SampleTypeProvider(config: TypeProviderConfig) as this =
...
//the below would be the generated type
type A<'b> () =
member this.C() : 'b = ...
member this.D() : 'b = ...
//
...
[<assembly:TypeProviderAssembly>]
do()
....
so that in the usage scenario would look something to
#r #".\bin\Debug\SampleTypeProvider.dll"
type A = SampleTypeProvider.A
type intA = A<int>
type strA = A<str>
And if that is possible - How can I approach it.
This is not possible using standard methods. I tried looking around, but could not find a canonical reference, but it is a known limitation and there have been various suggestions to lift the restriction.
Ross McKinlay has a somewhat extreme project called Mixin type provider that works around this by actually generating a file with F# source code when type provider is run (and you can then include this file in your project). This is perhaps more code-generation than type provider, but his talk about the topic is also a good explanation of some of the limitations.
How to address this very much depends on the purpose of the type provider. If you only need limited number of types, you could use something like static parameters and write A<"int"> or A<"string">. You could also mix ordinary non-provided generic types with non-generic provided types (in some clever way). But I think you need to write more about your concrete use case to get a better answer.

Is it possible to open a namespace provided by a Type Provider?

Is there any way to open a namespace of types provided by a type provider implementation? I ask, because some of the generated type name paths are very long and ugly in code. I have used type abbreviations to alleviate this, but obviously this is a manual process. The F# open keyword does not support this. Is there another way? Update: as pointed out in the answer and comments this is wrong. You can open a type provided namespace. I had not realised I was looking at deeply nested types, not a namespace.
This is tricky - parameterized type providers (like the standard SQL providers or the F# Data providers for XML and JSON) need to put all types that they generate (representing tables, XML nodes, etc) inside the main generated type. So all types that you might want to use are hidden somewhere as nested types of the main type (with parameters specified).
This makes sense - if you use the type provider with multiple parameters, the types generated for each configuration have to be separate.
As #kvb points out, you cannot open a nested type, but you can use type aliases to make this a bit less painful. For example, using F# Data, I can define an alias R that lets me access all the generated domain types with just two additional characters:
#r #"..\packages\FSharp.Data.1.1.10\lib\net40\FSharp.Data.dll"
open FSharp.Data
type RssFeed = XmlProvider<"http://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml">
type R = RssFeed.DomainTypes
let printTitle (itm:R.Item) = printfn "%A" itm.Title

Do F# 3.0 type providers use the DLR under the hood?

Do F# type providers work by using the DLR under the hood? That is to say, do they work in the way that the dynamic keyword in C# does? How is this related to expando objects?
How does codegen fit in?
Type providers are plugin to the compilation process. Internally a type provider may use DLR or anything but when the compiler ask it for a type it needs to return a type that is statically resolved at compile time. Think of it like rather than a human creating a type (class in C#) you have a assembly (type provider) which the compiler can ask to create a new type at compile time.
Ex: In case of SQL type provider the type representing the tables will be generated at compile time and put in the assembly as static types.
Type providers solve similar problem as the dynamic keyword in C# - both of them were designed to make it easier to access data that have some structure that is not described in your programming langauge and so you need to somehow infer it later.
The dynamic keyword just lets you access any member (i.e. data filed) or method at compile time and then decides how to handle the operation at runtime. If you're using it to access .NET object, then it will use DLR, but if you're accessing some other object (like JSON data) then it will perform some simple dictionary lookup.
F# type providers are quite different - they infer the structure at compile time and pass it to the F# compiler. The compiler will then check all your code. The type provider also decides how the access to a field or method should be compiled. Typically, it will either replace it with an ordinary .NET type (so it will be compiled as normal .NET invocation) or it will replace the object with some dictionary lookup. A type provider may use DLR under the cover, but I don't think it is very common case.

Resources