代码之家  ›  专栏  ›  技术社区  ›  Marko Grdinić

是否可以在运行时获取联合的标记?

  •  0
  • Marko Grdinić  · 技术社区  · 4 年前

    我正在写一个新版本的 Spiral language 作为其特征之一,它将具有类似于F#的结构平等性和比较性。但我很难弄清楚如何高效地编译成F。

    问题是,我找不到F#如何公开其联合案例的标记信息。

    例如,如果我写 cmp a b 我想让Spiral编译器生成如下的F代码。

    type T =
        | A of int32
        | B of float
    
    let cmp_t = function
        | A x, A x' -> cmp_i32 (x, x')
        | B x, B x' -> cmp_f64 (x, x')
        | x, x' -> cmp_i32 (union_tag x, union_tag x')
    

    我环顾四周,有一些建议,以揭露标签信息,但还没有出来。我想知道F#编译器库是否有一些我不知道的用于提取标记的功能。

    否则,对于每个联合类型,我将不得不生成一个自定义标记函数,如。。。

    let tag_t = function
        | A _ -> 0
        | B _ -> 1
    

    我该怎么办?

    0 回复  |  直到 4 年前
        1
  •  4
  •   Fyodor Soikin    4 年前

    你可以使用F#反射API来实现这个。

    Reflection.FSharpType.GetUnionCases 可以返回DU案例的完整列表,每个案例表示为 UnionCaseInfo 结构,具有以下有用特性 Name Tag :

    > Reflection.FSharpType.GetUnionCases(typeof<T>) |> Array.map (fun c -> c.Tag)
    
    [|0; 1|]  
    

    cmp_t 功能),签出 the Reflection.FSharpValue.GetUnionFields function 联合案例信息

    > let x = A 42
    > let caseInfo, fields = Reflection.FSharpValue.GetUnionFields(x, typeof<T>)
    > caseInfo.Tag
    
    0
    

    但是,请记住,对于F#判别并集,编译器将自动生成结构比较,因此 cmpt_t 功能冗余:

    > A 42 < A 54
    true
    
    > A 3 > B 1.0
    false
    
    // Or more generally:
    > (A 42 :> System.IComparable<T>).CompareTo(A 54)
    -1
    

    let cmp_t (x: 'a when 'a :> System.IComparable<'a>) y = 
        (x :> System.IComparable<'a>).CompareTo(y)
    
    > cmp_t (A 42) (A 54)
    -1
    

    看哪!标准库中已经有这样的函数。它叫 compare :

    > compare (A 42) (A 54)
    -1