漂亮的印刷歧视工会

Pretty print discriminated unions

我有一个 Option 类型:

type Option<'a> =
    | Some of value:'a
    | None

type MyString = string

// Syntax
type type-name =
    | case-identifier1 [of [ fieldname1 : ] type1 [ * [ fieldname2 : ] type2 ...]
    | case-identifier2 [of [ fieldname3 : ] type3 [ * [ fieldname4 : ] type4 ...]
...

我想用这样的格式打印一些东西:

"type-name: case-identifier(fieldname1:type1:'value1', fieldname2:type2:'value2')"

// Example:
let a : Option<MyString> = Some "1"
print a -> "Option: Some(value:MyString:'1')"
let b = None
print b -> "Option: None"

我之前问过一个,但我仍然无法获取字段的类型名称和类型。

在您的示例中,您将无法打印 MyString,因为这是一个类型别名,并且类型别名在编译代码中没有任何表示(仅在一些额外的 F# 元数据中,但这在这里没有帮助)。因此,在编译代码中,运行时只看到 string。我将改用单例 DU:

type Option<'a> =
    | Some of value:'a
    | None

type MyString = 
    | MyString of string
    override x.ToString() = let (MyString s) = x in s

let a = Some (MyString "1")
let b = None

根据您的示例格式化 DU 值的函数如下所示:

open Microsoft.FSharp.Reflection

let formatUnion<'T> (value:'T) =
    let case, args = FSharpValue.GetUnionFields(value, typeof<'T>)
    let args = 
        [| for f, v in Seq.zip (case.GetFields()) args ->
             sprintf "%s:%s='%O'" f.Name  f.PropertyType.Name v |]
    sprintf "%s: %s(%s)" (typeof<'T>.Name) case.Name (String.concat "," args)

这会打印 "Option'1: Some(value:MyString='1')" - 除了因为类型 Option 是泛型而存在的反引号之外,这看起来像你想要的东西 - 我没有做任何聪明的事情来处理嵌套类型,所以这是您仍然需要添加的一件事。