Print case-identifier of discriminated union

陌路散爱 提交于 2019-12-12 21:06:34

问题


I have an Option type:

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

    override x.ToString() = sprintf "%A" x

printf "%A" None // "None"
printf "%A" (Some 1) // "Some 1"

Supposedly, in a function I want to print Some 1, but in another function I want to print its case-identifier, i.e. Some (discard the " 1" value). How can I do it?


回答1:


If you want a generic way of doing it rather than implement a member for each of your types, you can use reflection for that:

open Microsoft.FSharp.Reflection

let caseLabel<'t> (x: 't) = 
    let typ = typeof<'t>
    if FSharpType.IsUnion(typ) 
        then
            let case, _ = FSharpValue.GetUnionFields(x, typ)
            Some case.Name
        else
            None



回答2:


You could use a match:

override x.ToString() = 
    match x with
    | Some _ -> "Some"
    | None -> "None"

To address your comment: In this case I wouldn't override the ToString method, but instead do separate matches depending on the intended behavior. Or just define a helper function for printing an option without specifying content;

let printEmpty myOpt = 
    match myOpt  with
    | Some _ -> "Some"
    | None -> "None"

That way you could either use sprintf "%A" myOpt to print with content, or printEmpty myOpt to print without.




回答3:


For what it's worth, I'm including @scrwtp's version with a "type extension" form, rather than the original reimplementation of Option<'a>. The rationale being simply that the most recently defined type confused the use of caseLabel within the ToString portion (of the type Option<'a> definition in the original question).

NB. because we can't inherit from a DU (it has no constructors), we can't overload ToString. So extensions with ToString still default to the original ToString. Therefore, the static module style is probably more desirable, as we can explicitly access the new behaviour. Otherwise, the behaviour of sprintf / printf will access the original ToString, which is not what we want as per the question. I consider this a compiler bug. Bug filed over here

btw: FSI pretty printing could alleviate this for FSI-only scenarios (TBD).

open Microsoft.FSharp.Reflection

let caseLabel (x:'x) = 
    typeof<'x> |> fun typ ->
      if FSharpType.IsUnion(typ) 
          then FSharpValue.GetUnionFields(x, typ) ||> fun case _ -> Some(case.Name)
          else None

type Option<'t> with 
  static member toString x = 
    match caseLabel x with
    | Some(label) -> label
    | None        -> "None"  

sprintf "%s" <| (Some 1 |> Option.toString)  // returns "Some"
sprintf "%s" <| (None |> Option.toString)    // returns "None"


来源:https://stackoverflow.com/questions/43981211/print-case-identifier-of-discriminated-union

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!