问题
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