Вы можете получить тип функции F #, используя Compiler Services SDK .Это потребует написания специального анализатора для ваших проектов, но это должен быть компонент многократного использования, который вы сможете интегрировать в процесс разработки после внедрения.Основные шаги для разрешения сигнатуры типа каждой функции:
- Создание экземпляра F # Type Checker (
FSharpChecker
). - Загрузка параметров проекта (
FSharpProjectOptions
). - Разобрать и проверить каждый файл (
FSharpChecker.parseAndCheckFileInProject
). - Получить список объявлений из каждого результата проверки типов (
FSharpCheckFileAnswer
). - Распечатать подпись типа (
FSharpType
) для каждого объявления.
Вот быстрое решение, которое я собрал в качестве отправной точки:
#r @"FSharp.Compiler.Service.25.0.1\lib\net45\FSharp.Compiler.Service.dll"
#r @"FSharp.Compiler.Service.ProjectCracker.25.0.1\lib\net45\FSharp.Compiler.Service.ProjectCracker.dll"
open Microsoft.FSharp.Compiler.SourceCodeServices
open System
open System.IO
type Namespace =
{
Name: string
XmlDoc: System.Collections.Generic.IList<string>
}
type Declaration =
| Namespace of Namespace * Declaration list
| Module of FSharpEntity * Declaration list
| Class of FSharpEntity * Declaration list
| Interface of FSharpEntity * Declaration list
| Enum of FSharpEntity * Declaration list
| Record of FSharpEntity * Declaration list
| Union of FSharpEntity * Declaration list
| Function of FSharpMemberOrFunctionOrValue
| Binding of FSharpMemberOrFunctionOrValue
let checker = FSharpChecker.Create(1, true)
let getProject projectFile =
ProjectCracker.GetProjectOptionsFromProjectFile(projectFile)
let private isNamespace (declaration: FSharpImplementationFileDeclaration) =
match declaration with
| FSharpImplementationFileDeclaration.Entity (entity, children) -> entity.IsNamespace
| _ -> false
let rec private getDeclaration nsSoFar (declaration: FSharpImplementationFileDeclaration) =
[
match declaration with
| FSharpImplementationFileDeclaration.Entity (entity, children) ->
if entity.IsNamespace then
if children.Length = 1 && children.Head |> isNamespace
then match nsSoFar with
| Some ns -> yield! getDeclaration (Some <| sprintf "%s.%s" ns entity.DisplayName) children.Head
| None -> yield! getDeclaration (Some entity.DisplayName) children.Head
else match nsSoFar with
| Some ns ->
let nsEntity = {Name = sprintf "%s.%s" ns entity.DisplayName; XmlDoc = entity.XmlDoc}
yield Namespace (nsEntity, children |> List.collect (getDeclaration nsSoFar))
| None ->
let nsEntity = {Name = entity.DisplayName; XmlDoc = entity.XmlDoc}
yield Namespace (nsEntity, children |> List.collect (getDeclaration nsSoFar))
elif entity.IsClass then
yield Class (entity, children |> List.collect (getDeclaration nsSoFar))
elif entity.IsInterface then
yield Interface (entity, children |> List.collect (getDeclaration nsSoFar))
elif entity.IsEnum then
yield Enum (entity, children |> List.collect (getDeclaration nsSoFar))
elif entity.IsFSharpModule then
yield Module (entity, children |> List.collect (getDeclaration nsSoFar))
elif entity.IsFSharpRecord then
yield Record (entity, children |> List.collect (getDeclaration nsSoFar))
elif entity.IsFSharpUnion then
yield Union (entity, children |> List.collect (getDeclaration nsSoFar))
else
()
| FSharpImplementationFileDeclaration.MemberOrFunctionOrValue (func, _, _) ->
if func.IsValCompiledAsMethod
then yield Function func
else yield Binding func
| _ -> ()
]
let getDeclarations (project: FSharpProjectOptions) file =
async {
let source = File.ReadAllText file
let! (parseResults, checkResults) = checker.ParseAndCheckFileInProject(file, 1, source, project)
return
match checkResults with
| FSharpCheckFileAnswer.Succeeded checkInfo ->
match checkInfo.ImplementationFile with
| Some implementation -> implementation.Declarations |> List.collect (getDeclaration None)
| None -> failwithf "No Implementation Available for File %s" file
| error -> failwithf "Error Checking File %s:\r\n%A" file error
}
let getDeclarationsForScript file =
async {
let source = File.ReadAllText file
let! (project, _) = checker.GetProjectOptionsFromScript(file, source)
return! getDeclarations project file
}
Затем, если у нас есть пример файла сценария с именем «Test».fsx "с функцией наподобие вашего примера (let f i = sprintf "%d" i
), мы можем напечатать сигнатуру функции следующим образом:
let getTypeName (t: FSharpType) =
t.Format(FSharpDisplayContext.Empty).Replace("Microsoft.FSharp.Core.", "")
let rec printFunctionSignatures declarations =
for declaration in declarations do
match declaration with
| Namespace (_, ds) -> printFunctionSignatures ds
| Module (_, ds) -> printFunctionSignatures ds
| Function f -> f.FullType |> getTypeName |> printfn "%s: %s" f.DisplayName
| _ -> () // Handle all the other cases
getDeclarationsForScript "Test.fsx"
|> Async.RunSynchronously
|> printFunctionSignatures
Это выведет:
f: int -> string