Различают в кавычках обобщенное значение «вызов» и вызов универсальной функции - PullRequest
2 голосов
/ 12 марта 2011

Учитывая следующее:

let f<'a,'b> = typeof<'a>.Name, typeof<'b>.Name //val f<'a,'b> : string * string
let g<'a,'b>() = typeof<'a>.Name, typeof<'b>.Name //val g<'a,'b> : unit -> string * string

следующие цитаты дают то, что кажется идентичным Expr s:

let fq = <@ f<int,string> @> //Call (None, System.Tuple`2[System.String,System.String] f[Int32,String](), [])
let gq = <@ g<int,string>() @> //Call (None, System.Tuple`2[System.String,System.String] g[Int32,String](), [])

Копаясь с отладчиком, я не вижу способа сказать, что f - это универсальное значение, тогда как g - универсальная функция: возможно ли это сказать только из fq и gq ? Поскольку F # может определить разницу между f и g в разных сборках, я думаю, что есть хороший шанс получить метаданные из цитаты. Хотя проблема может заключаться в том, что F #, по-видимому, на самом деле компилирует версию f, которая является функцией unit -> string * string (рассматривает дизассемблированный код; предположительно для взаимодействия с другими языками .NET), поэтому, если в цитате используется версия функции , информация может быть потеряна.

Обновление

Под руководством @ Томаса вот что я придумала:

let isGenericValue (mi:MemberInfo) =
    try
        let mOrV =
            FSharpEntity.FromType(mi.DeclaringType).MembersOrValues
            |> Seq.find (fun mOrV -> mOrV.CompiledName = mi.Name)

        not mOrV.Type.IsFunction
    with
    | :? System.NotSupportedException -> true //for dynamic assemblies, just assume idiomatic generic value

Это можно использовать для сопоставления Patterns.Call(_,mi,_), где второй аргумент mi является экземпляром MemberInfo. Однако есть одна проблема: она не работает для динамических сборок (например, FSI).

1 Ответ

3 голосов
/ 12 марта 2011

Под прикрытием обобщенные значения компилируются как обобщенные методы, потому что среда выполнения .NET не имеет понятия «универсальные поля» (или что-то в этом роде).Я думаю, что F # различает два, используя информацию, хранящуюся в двоичном двоичном объекте «FSharpSignatureData» в ресурсах.

Способ работы с этой двоичной информацией заключается в использовании средства чтения метаданных F # из F # PowerPack .Если вы компилируете две строки, которые вы пишете в test.exe, то вы пишете это:

let met = Microsoft.FSharp.Metadata.FSharpAssembly.FromFile(@"C:\temp\test.exe")
for e in met.Entities do
  // Prints 'Test' for the top-level module
  printfn "%A" e.DisplayName 
  for e in e.MembersOrValues do
    // Prints:
    //  "f" false (for value 'f')
    //  "g" true  (for function 'g')
    printfn "- %A %A" e.DisplayName e.Type.IsFunction

Если вы хотите различать две эти функции с помощью отражения, вам нужно будет найти какой-нибудь способ соединенияинформация метаданных с отражением MethodInfo (что, вероятно, можно сделать с помощью свойства CompiledName).

...