Типовые и динамические универсальные типы

#generics #casting #f#

Вопрос:

Я пытаюсь использовать TypeSharp. До сих пор мне приходилось использовать shapeof<'T> только следующим образом:

 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"
 

Но теперь я хотел бы создать динамически shapeof , как

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

Поэтому я подумал, что мог бы сделать что-то вроде этого

 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 
 

ошибка в строке shape :?> ShapeFSharpRecord<_> выглядит следующим образом:

 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]]'.'
 

Я тоже попробовал этот трюк:

 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 
 

Ошибка на линии 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]]'.'
 

Я понятия не имею, откуда это Microsoft.FSharp.Core.FSharpOption берется.
Есть идеи, как я мог бы динамически создать экземпляр ShapeFSharpRecord?

Комментарии:

1. Есть только одно место, откуда может исходить опция: тип foo . Вы не показали код для foo , но полученное сообщение об ошибке определенно указывает на то, что foo ожидается параметр типа ShapeFSharpRecord<AwsConfig option>

2. Это правда! Я имею в виду, что на самом foo деле ожидает ShapeFSharpRecord<AwsConfig> , но когда я удаляю foo shape строку, я получаю ошибку 'Unable to cast object of type 'ShapeFSharpRecord`1[FsConfig.Tests.Common AwsConfig]' to type 'ShapeFSharpRecord`1[System.Object]'.'

3. foo определенно не ожидает того, чего, по вашим словам, он ожидает. Он определенно ожидает тип, включающий option . Если вы покажете код foo , возможно, я смогу указать на это более точно. Если вы удалите вызов to foo , то при отсутствии каких-либо оснований для вывода целевого типа компилятор вернется к Object .

4. Точка зрения Бартека заключается в том, что foo это не имеет значения и может быть полностью удалено. Проблема в том, что динамическая версия кода ( TypeShape.Create ) завершается неудачно, в то время как статическая версия ( shapeof<AwsConfig> ) завершается успешно.

5. Просто попробуй shape :?> ShapeFSharpRecord<AwsConfig> . Вы получите вызов ошибки во время компиляции foo , но само приведение будет работать. Удалите foo вызов для проверки.

Ответ №1:

Я ничего не знаю о типографии, но я просто немного просмотрел ее исходный код и воспроизвел то, что вы видите. К сожалению, я думаю, что короткий ответ просто «вы не можете этого сделать».

Чтобы быть более конкретным, если вы хотите получить доступ к базовой форме an FSharpRecord , вам необходимо знать ее тип во время компиляции. Если вы попытаетесь усовершенствовать его , приведя к ShapeFSharpRecord<_> , компилятор не сможет определить тип и вместо этого использует obj (т. Е. System.Object ).

Итак, фактический тип фигуры таков ShapeFSharpRecord<AwsConfig> , но вы пытаетесь привести ее к a ShapeFSharpRecord<obj> , что невозможно сделать и приводит к исключению во время выполнения.

(Я игнорирую проблемы, связанные с подписью foo функции, которые, по-моему, не являются центральными в вашем вопросе.)

Комментарии:

1. пока я просто откажусь от неудачи 🙂 пока я пытаюсь узнать больше о точке зрения @fyodor, но как только я закончу, я подумаю о том, чтобы уступить