Bartek Wójcik
Bartek Wójcik

Reputation: 425

TypeShape and dynamic generics

I'm trying to use TypeSharp. So far, I only had to use shapeof<'T> in a following manner:

type AwsConfig = {
    AccessKeyId : string
    DefaultRegion : string
    SecretAccessKey : string
  }


let targetTypeShape = shapeof<AwsConfig> //targetTypeShape is of type targetTypeShape<AwsConfig>
match targetTypeShape with
| Shape.FSharpRecord (:? ShapeFSharpRecord<AwsConfig> as shape) -> foo shape   
// 'shape' is of type ShapeFSharpRecord<AwsConfig>, which is important //             
| _ -> failwith "some other cases"

But now I'd like to create dynamically shapeof like

let awsConfigInstance = {AccessKeyId="123";DefaultRegion="asd";SecretAccessKey="asddd"}
let awsType = awsConfigInstance.GetType()
let targetTypeShape = shapeof<awsType> //that doesn't compile obviously

So i though i could do something like this

let typeShape = TypeShape.Create (awsConfigInstance.GetType())
match typeShape with
//'shape' is of type IShapeFSharpRecord not ShapeFSharpRecord<AwsConfig> as i need it too be
| Shape.FSharpRecord shape->                                
                      let castedShape = shape :?> ShapeFSharpRecord<_> //error
                      foo castedShape 

the error on line shape :?> ShapeFSharpRecord<_> is as follows:

System.InvalidCastException: 'Unable to cast object of type 
'ShapeFSharpRecord`1[FsConfig.Tests.Common+AwsConfig]' to type 
'ShapeFSharpRecord`1[Microsoft.FSharp.Core.FSharpOption`1[FsConfig.Tests.Common+AwsConfig]]'.'

I also tried to this trick:

let typeShape = TypeShape.Create (awsConfigInstance.GetType())
match typeShape with
//'shape' is of type IShapeFSharpRecord not ShapeFSharpRecord<AwsConfig> as i need it too be
| Shape.FSharpRecord shape-> 
                      let temp = Activator.CreateInstanceGeneric<ShapeFSharpRecord<_>>([|typeShape .Type|], [||])
                      let castedShape = temp :?> ShapeFSharpRecord<_> //error
                      foo castedShape 

Error is on line let castedShape = temp :?> ShapeFSharpRecord<_>

'Unable to cast object of type 'ShapeFSharpRecord`1[FsConfig.Tests.Common+AwsConfig]' 
to type 'ShapeFSharpRecord`1[Microsoft.FSharp.Core.FSharpOption`1[FsConfig.Tests.Common+AwsConfig]]'.'

I have no clue where is Microsoft.FSharp.Core.FSharpOption coming from. Any ideas how could i dynamically create ShapeFSharpRecord instance?

Upvotes: 0

Views: 115

Answers (1)

Brian Berns
Brian Berns

Reputation: 17038

I don't know anything about TypeShape, but I've just looked at its source code a bit and reproduced what you're seeing. Unfortunately, I think the short answer is just "you can't do that".

To be more specific, if you want access to the underlying shape of an FSharpRecord, then you have to know its type at compile time. If you try to finesse it by casting to ShapeFSharpRecord<_>, the compiler isn't able to infer the type and uses obj (i.e. System.Object) instead.

So, the actual type of the shape is ShapeFSharpRecord<AwsConfig>, but you're trying to cast it to a ShapeFSharpRecord<obj>, which can't be done and results in a runtime exception.

(I'm ignoring issues related to the signature of the foo function, which I don't think are central to your question.)

Upvotes: 1

Related Questions