FSharp.TypeProviders.SDK icon indicating copy to clipboard operation
FSharp.TypeProviders.SDK copied to clipboard

Emit Discriminated Unions

Open JaggerJo opened this issue 6 years ago • 7 comments

I'm currently playing around with building a DSL for Avalonia UI. I looked at how Fabulous generates types one time.

It would be interesting to create the DSL dynamically using a (generative) type provider. In my case I would like to generate DU's for all implementations of IControl.

type ButtonAttr =
| Name of string
| Width of float
…

This would make it extremely simple to use 3rd party controls, because bindings are generated at compile/ design time.

Currently it is (as far as I know) not possible to emit F# specific types like Records or Discriminated Unions from a Type Provider.

  1. Why isn't it possible to generate F# specific types (aren't they just nested classes with custom attributes/metadata after all) ?
  2. Is there a general interest in making this possible (or is it just me wanting this) ?

EDIT: just found out there is a discussion about using a TP in Fabulous

JaggerJo avatar Apr 27 '19 19:04 JaggerJo

There is a language suggestion regarding provided DU & records: https://github.com/fsharp/fslang-suggestions/issues/154

And another one to be able to pass types to type providers: https://github.com/fsharp/fslang-suggestions/issues/212

smoothdeveloper avatar Apr 28 '19 05:04 smoothdeveloper

  1. Why isn't it possible to generate F# specific types (aren't they just nested classes with custom attributes/metadata after all) ?

The main thing is that it's just a lot of work. The way TPs are architected (providing .NET System.Type/MethodInfo etc. and the compiler converts them to F# TAST nodes) effectively means we have to fully invert the compilation process from .NET IL types --> F# TAST types. This is implemented in tast.fs and infos.fs in the F# compiler for methods, types, properties, events but there is no corresponding inversion for F#-defined union/record types.

The inversion would be similar to that implemented in FSharp.Reflection.FSharpType/FSharpValue based on the attributes etc. But it's painful to implement and fully test accurately.

  1. Is there a general interest in making this possible (or is it just me wanting this) ?

Yes, people are interested in this

dsyme avatar May 07 '19 13:05 dsyme

@dsyme Thank you for the explanation (If you don't mind I have on further question).

If generating TAST in F# would be simple enough, the TAST could be given straight to the Compiler, then compilation could happen as usual. Why is it the other way around (Type/MethodInfo -> TAST) ?

JaggerJo avatar May 08 '19 16:05 JaggerJo

If generating TAST in F# would be simple enough, the TAST could be given straight to the Compiler, then compilation could happen as usual. Why is it the other way around (Type/MethodInfo -> TAST) ?

The TAST is an internal compiler representation and not deemed to be sufficiently stable nor simple enough to allow direct generation.

dsyme avatar May 13 '19 14:05 dsyme

Thanks for the explanation, should I close this now ?

JaggerJo avatar Jul 19 '19 09:07 JaggerJo

@JaggerJo It's ok to leave it open I think

dsyme avatar Jul 19 '19 13:07 dsyme

What about providing the FSharp compiler services interface to a TypeProvider, so it can get better metadata? And with ProvidedDU and ProvidedRecords, that would increase the flexibility. Perhaps we could have a DSL for creating the relevant part of the TypeProvider output that will become the TAST. (of course you wouldn't expose the real TAST) I don't mind it being internally represented by Type/MethodInfo, but wouldn't it be simpler having a specific AST/DSL for TypeProviders ? (nothing impedes someone doing that and it creating the Quotation expressions needed, that's precisely how I'm thinking to tackle my TypeProvider ) [edit: after reading other topics, I guess I have my historical reasons answer]

But FSharpCompilerServiceTypeProvider, that would be a fun thing !

Luiz-Monad avatar Feb 05 '20 23:02 Luiz-Monad