Trying to encode type-level peano numbers using a type provider:
namespace TypeProviderPlayground
open System
open Microsoft.FSharp.Core.CompilerServices
open System.Runtime.CompilerServices
[<assembly: TypeProviderAssembly()>]
do()
type Z = class end
type 'a S = class end
type N = class end
[<TypeProvider>]
type PeanoProvider(s: TypeProviderConfig) =
let invalidate = Event<_,_>()
interface ITypeProvider with
member x.ApplyStaticArguments(typeWithoutArguments, typeNameWithArguments, staticArguments) =
let n : int = unbox staticArguments.[0]
[1..n] |> List.fold (fun s _ -> typedefof<S<_>>.MakeGenericType [| s |]) typeof<Z>
member x.GetNamespaces() =
let ns =
{ new IProvidedNamespace with
member x.GetNestedNamespaces() = [||]
member x.GetTypes() = [||]
member x.ResolveTypeName t =
if t = "N"
then typeof<N>
else null
member x.NamespaceName = "Peano" }
[| ns |]
member x.GetStaticParameters t =
let p =
{ new Reflection.ParameterInfo() with
member z.Name = "number"
member z.ParameterType = typeof<int> }
[| p |]
[<CLIEvent>]
member x.Invalidate = invalidate.Publish
member x.Dispose() = ()
member x.GetInvokerExpression(syntheticMethodBase, parameters) =
raise <| NotImplementedException()
The N
type is just a dummy, otherwise I couldn't get it to go through the type provider.
Consumer code:
open TypeProviderPlayground
[<Generate>]
type S<'a> = Peano.N<5>
And I get this error:
error FS3152: The provider 'TypeProviderPlayground.PeanoProvider' returned a non-generated type
'TypeProviderPlayground.S`1[[TypeProviderPlayground.S`1[[TypeProviderPlayground.S`1[[TypeProviderPlayground.S`1[[TypeProviderPlayground.S`1[[TypeProviderPlayground.Z, TypeProviderPlayground, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]], TypeProviderPlayground, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]], TypeProviderPlayground, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]], TypeProviderPlayground, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]], TypeProviderPlayground, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]'
in the context of a [<Generate>] declaration. Either remove the [<Generate>] declaration or adjust the type provider to only return generated types.
Which says that the type was correctly constructed (Z S S S S S
) but for some reason the compiler won't accept it as a "generated type".
If I remove the [<Generated>]
attribute I get some other error telling me to add it.
Does this mean that type providers will only work on dynamically emitted types (which seems a weird requirement at first blush)?
Also, if I do:
[<Generate>]
type WW<'a> = Peano.N<5>
I get an error saying that WW'1
was expected but S'1
was returned. Why does the returned type (by the type provider) have to match the type name I declare in the consumer?
There are a few important things to realize about type providers. First of all, there are two kinds of provided types:
Just as a heads-up, the mechanisms for controlling this distinction are still somewhat up in the air. In the preview, you need to use the
[<Generate>]
attribute in the assembly into which generated types are being embedded, and you should not use the[<Generate>]
attribute when using an erased provided type. I believe (but can't remember for sure off hand) that on the provided end generated-ness is determined based on the type'sAssembly
property.Also, keep in mind that you don't necessarily want to use actual types (e.g. via
typeof<X>
) when implementing the API - you'll frequently want to use custom types derived fromSystem.Type
. There are a lot of invariants that must be satisfied among the different methods. The raw type provider API is not easy to use - I'd suggest waiting for some examples to be released which use a nicer API wrapper (which I hope should take place within the next few weeks).Having said that, from a quick look here are at least a few things in your current approach which look wrong to me:
ApplyStaticArguments
doesn't have the same name as the argumenttypeNameWithArguments
. Presumably this is why you're getting the error mentioning the type names.WW<'a>
) from a non-generic type (e.g.S<S<S<S<S<Z>>>>>
).Forgot to update on this: indeed what was missing was the 'erased' type flag (
TypeProviderTypeAttributes.IsErased
) in my 'exported' type. I put my experiments on github.