【问题标题】:string representation of F# function signatureF# 函数签名的字符串表示
【发布时间】:2019-08-22 04:32:57
【问题描述】:

当我在 F# REPL fsharpi 中工作时,只要我输入一个新函数,就会在我输入它们后打印签名:

> let foo x = x;;
val foo : x:'a -> 'a

有没有办法将它作为字符串检索?我问的原因是我将 IfSharp 用于不显示签名的 Jupyter 笔记本,但我希望能够显示函数类型以用于演示目的。

我搞砸了一点,但没有得到任何有用的东西,我试过了:

let foo x = (x, x)
printfn "%A" (foo.GetType())
printfn "%A" foo

但这并不是我所需要的:

FSI_0013+clo@3-1
<fun:it@5-2>

是否可以访问它?

【问题讨论】:

  • 不幸的是,我不确定 F# 库中是否真的有一个函数可以做到这一点。但是,为此编写自己的函数可能并不难。
  • Dotnet Interactive 以nuget package 的形式提供。它可能有一个很好的方法
  • F# 交互也暴露在compiler nuget packageFsiValuePrinter 看起来很有希望。

标签: f# jupyter let


【解决方案1】:

AFAIK,FSharp.Core 中没有用于获取类型的字符串表示的函数,因为它会出现在编译器中(尽管 FSharp.Compiler.Services 中可能有一些东西——我没有检查过)。这是一个适用于大多数简单用途的小函数:

open System

let (|TFunc|_|) (typ: Type) =
    if typ.IsGenericType && typ.GetGenericTypeDefinition () = typeof<int->int>.GetGenericTypeDefinition () then
        match typ.GetGenericArguments() with
        | [|targ1; targ2|] -> Some (targ1, targ2)
        | _ -> None
    else
        None

let rec typeStr (typ: Type) =
    match typ with
    | TFunc (TFunc(_, _) as tfunc, t) -> sprintf "(%s) -> %s" (typeStr tfunc) (typeStr t)
    | TFunc (t1, t2) -> sprintf "%s -> %s" (typeStr t1) (typeStr t2)
    | typ when typ = typeof<int> -> "int"
    | typ when typ = typeof<string> -> "string"
    | typ when typ.IsGenericParameter -> sprintf "'%s" (string typ)
    | typ -> string typ


typeStr typeof<(string -> (string -> int) -> int) -> int>
// val it: string = "string -> (string -> int) -> int"
typeStr (typeof<int->int>.GetGenericTypeDefinition())
// val it: string = "'T -> 'TResult"

您可以轻松地在此基础上编写一个函数以在值的类型上使用typeStr

let valTypeString x = typStr (x.GetType ())

【讨论】:

    【解决方案2】:

    您可以在Microsoft.FSharp.Reflection 命名空间的帮助下分析表示 F# 函数的类型。需要注意的是,通用函数参数默认为 System.Object,并且不包括可能形成不完整模式的其他 F# 类型(例如联合案例、记录)。

    open Microsoft.FSharp.Reflection
    let funString o =
        let rec loop nested t =
            if FSharpType.IsTuple t then
                FSharpType.GetTupleElements t
                |> Array.map (loop true)
                |> String.concat " * "
            elif FSharpType.IsFunction t then
                let fs = if nested then sprintf "(%s -> %s)" else sprintf "%s -> %s"
                let domain, range = FSharpType.GetFunctionElements t
                fs (loop true domain) (loop false range)
            else
                t.FullName
        loop false (o.GetType())
    
    let foo x = x
    funString foo
    // val it : string = "System.Object -> System.Object"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-20
      • 2016-03-09
      • 2020-09-04
      • 2018-05-22
      • 1970-01-01
      相关资源
      最近更新 更多