I'm playing with F#'s quotation feature recently, and I find it's really powerful because we can create a lambda function based on expression tree and eval it to a real function, this is much like macros in other languages. Unfortunately, it's unavailable in Fable.
Here are some cases that I would use some equivalent feature in Fable:
F#
type People = {
name: string
age: int
}
module People =
let empty =
{ name = ""
age = 0 }
In F#, I need to write lots' of empty record, it could actually be done by Reflection or quotations, but Fable's reflection API is very limit, I'm not sure there is a way to do this. And since Fable 2.0 will use more lightweight types, plugins API with full access to generic type info might be a good idea.
I usually create lots of Records/Classes, like BookInput (book model for input form), BookOutput(book model for output), BookEntity (book entity for ORM) and need write lots of functions to transform between them. The transform functions can be generated by quotations, but there still are some in fables that I'm not sure how to implement, like BookOutput -> BookInput.
Fable's interop API is very powerful and convenient, but as same as No.1, without type information it could be really hard to handle FSharp values/types.
I know reflection, plugins are not included in the alpha release, but maybe it's the time for discussion? :smile:
Thanks for opening this discussion @zaaack! The current story of metaprogramming in Fable 2 is... there's no story :wink: In Fable 1, all F# types were translated to JS classes/function constructors with extra reflection info attached. In Fable 2 this won't be possible for records and unions, so I was thinking to leave reflection/metaprogramming aside for now and add it a later stage as a an _extra_ that doesn't penalize general users. But I'm not sure yet how to do it, so it's good that we start some discussion. Some ideas are:
After confirming that erased type providers work with latest FCS on netstandard I'm currently more leaned towards the third option, as it doesn't force users to know Fable internals (writing a TP is also hard though) and some type providers could be shared between Fable and .NET.
Giving full support of quotations at runtime in Fable would be difficult. I had an early experiment where quotations where translated to Babel AST and then you could manipulate them with Babel API, but I discarded it. To be able to manipulate F# quotations at runtime in JS we would need almost full compatibility with .NET reflection and other things, which is a lot of work. However, some kind of manipulation of quotations _at compile time_ would be possible, but in those cases it's usually easier to manipulate normal code (for example, Fable already supports the nameof operator). You can also use quotations with type providers.
@alfonsogarciacaro It is possible to make the inclusion of reflection metadata an option of the compilation process with a default of false?
The type provider looks really promising, just a few questions:
1.Would it possible to pass record type to TP's as static parameters?
If 2 is cann't, too, for cases I mentioned above, I guess I need to put my domain types in the TP project, or using a string dsl to create class types, but Im afraid it's not suitable for elmish's immutable model.
I'm also wondering the possibility of implementing a quotation-based macro system in Fable, e.g.
```F#
[
let emptyOf<'T> (): 'T =
let pis = FSharpType.GetRecordFields(typeof<'T>)
...
let expr: Expr = ... // the quotation expression tree that express the empty record of type 'T
eval expr :?> 'T
// usage
type People = {
name: string
age: int
}
module People =
let empty = emptyOf
```
This code can be shared between dotnet core and fable, in dotnet the expression tree is evaluated at runtime, but in fable it will be evaluated at compile time, just like macros.
Edit: this might be a bad idea, as far as I can see, the benefits of the quotations is we can generate fsharp code in runtime, for fable is we can generate type-safe fable code that doesn't need to know the detail of the generated js code. If TP is powerful enough I think TP already has all the benefits.
@Zaid-Ajaj For that, we need to add first the metadata before making it optional :) But in any case, I don't think it's a question of all or nothing, because in most cases users would need reflection for only a few types. Also, just to let you know, I'm very interested in making Fable.Remoting work with Fable 2 (stable) out-of-box or with little changes. I already had a look at the source and I will try to find if it's possible to resolve the needed reflection info at compile time.
@zaaack I'm not totally sure, but I guess TP accept types as static parameters. Attribute constructor arguments are also static and beside literals (strings and numbers) they also accept types.
@alfonsogarciacaro That would make me really happy :heart:
TypeProviders don't accept other types as static parameters.
https://stackoverflow.com/questions/9547225/can-i-provide-a-type-as-an-input-to-a-type-provider-in-f
This can be closed right?
I guess so, thanks @Zaid-Ajaj!
Most helpful comment
@Zaid-Ajaj For that, we need to add first the metadata before making it optional :) But in any case, I don't think it's a question of all or nothing, because in most cases users would need reflection for only a few types. Also, just to let you know, I'm very interested in making Fable.Remoting work with Fable 2 (stable) out-of-box or with little changes. I already had a look at the source and I will try to find if it's possible to resolve the needed reflection info at compile time.
@zaaack I'm not totally sure, but I guess TP accept types as static parameters. Attribute constructor arguments are also static and beside literals (strings and numbers) they also accept types.