как получить подпись f # - visual studio или vs код - PullRequest
0 голосов
/ 30 января 2019

Я хочу иметь возможность явно записывать сигнатуры типов в моем коде.

Код VS будет (в конечном итоге) генерировать разновидности фиктивных сигнатур, но на самом деле я хочу явно взять эти сгенерированные сигнатуры и ввести код.

Есть идеи?Я мог бы использовать FSI, но это может быть довольно громоздким методом.

В идеале я бы щелкнул правой кнопкой мыши и "сгенерировал бы подпись" ... хотя это не всегда соответствует стилю кодирования людей ... Я склонен писатькод;

   let f : int -> string = 
      fun i -> i.ToString()

1 Ответ

0 голосов
/ 31 января 2019

Вы можете получить тип функции F #, используя Compiler Services SDK .Это потребует написания специального анализатора для ваших проектов, но это должен быть компонент многократного использования, который вы сможете интегрировать в процесс разработки после внедрения.Основные шаги для разрешения сигнатуры типа каждой функции:

  1. Создание экземпляра F # Type Checker (FSharpChecker).
  2. Загрузка параметров проекта (FSharpProjectOptions).
  3. Разобрать и проверить каждый файл (FSharpChecker.parseAndCheckFileInProject).
  4. Получить список объявлений из каждого результата проверки типов (FSharpCheckFileAnswer).
  5. Распечатать подпись типа (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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...