简体   繁体   English

如何获得F#签名-Visual Studio或vs代码

[英]how to get f# signature - visual studio or vs code

I want to be able to explicitly write type signatures in my code. 我希望能够在我的代码中显式地编写类型签名。

VS code will (eventually) generate sort of ghost signatures, but I actually want to explicitly take these generated signatures and type the code. VS代码将(最终)生成某种重影签名,但是我实际上想明确地采用这些生成的签名并键入代码。

Any ideas? 有任何想法吗? I could use FSI, but that can be quite a cumbersome technique. 我可以使用FSI,但这可能是一项繁琐的技术。

Ideally I'd right click and "generate signature"..though that doesn't always fit peoples coding style...I tend to write code; 理想情况下,我会右键单击并“生成签名”。尽管这并不总是适合人们的编码风格,但我倾向于编写代码;

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

You can get the type of an F# function using the Compiler Services SDK . 您可以使用Compiler Services SDK获得F#函数的类型。 This would require writing a custom analyzer for your projects, but it should be a reusable component that you can integrate into your development process once implemented. 这将需要为您的项目编写一个自定义分析器,但是它应该是可重用的组件,您可以在实现后将其集成到开发过程中。 The basic steps to resolve every function's type signature would be: 解决每个函数的类型签名的基本步骤是:

  1. Create an F# Type Checker ( FSharpChecker ) instance. 创建一个F#类型检查器( FSharpChecker )实例。
  2. Load your project options ( FSharpProjectOptions ). 加载您的项目选项( FSharpProjectOptions )。
  3. Parse and check each file ( FSharpChecker.parseAndCheckFileInProject ). 解析并检查每个文件( FSharpChecker.parseAndCheckFileInProject )。
  4. Retrieve the Declarations list from each type-checker result ( FSharpCheckFileAnswer ). 从每个类型检查器结果( FSharpCheckFileAnswer )中检索声明列表。
  5. Print the type signature ( FSharpType ) for each declaration. 为每个声明打印类型签名( FSharpType )。

Here's a quick solution I put together as a starting point: 这是我汇总的一个快速解决方案:

#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
    } 

Then, if we have a sample script file called "Test.fsx" with a function like your example inside it ( let fi = sprintf "%d" i ), we can print the function's signature like so: 然后,如果我们有一个名为“ Test.fsx”的示例脚本文件,其中包含一个类似于您的示例的函数( let fi = 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

This will pint out: 这将指出:

f: int -> string

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM