Print case-идентификатор дискриминируемого объединения


У меня есть Option Тип:

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

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

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

Предположительно, в функции я хочу напечатать Some 1, но в другой функции я хочу напечатать ее case-идентификатор, т. е. Some (отбросьте значение "1"). Как я могу это сделать?

3 2

3 ответа:

Если вы хотите сделать это универсальным способом, а не реализовывать член для каждого из ваших типов, вы можете использовать отражение для этого:

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

Можно использовать спичку:

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

Чтобы ответить на ваш комментарий: В этом случае я бы не переопределял метод ToString, а вместо этого делал отдельные совпадения в зависимости от предполагаемого поведения. Или просто определите вспомогательную функцию для печати опции без указания содержимого;

let printEmpty myOpt = 
    match myOpt  with
    | Some _ -> "Some"
    | None -> "None"
Таким образом, вы можете либо использовать sprintf "%A" myOpt для печати с содержимым, либо printEmpty myOpt для печати без него.

Для чего бы это ни стоило, я включаю версию @scrwtp с формой "расширение типа", а не оригинальное переосмысление опции. Причина заключается просто в том, что самый последний определенный тип перепутал использование caseLabel в части ToString (определения типа Option в исходном вопросе).

NB. поскольку мы не можем наследовать от DU (у него нет конструкторов), мы не можем перегружать ToString. Поэтому наращивание с помощью метода toString еще по умолчанию в оригинальной Метод toString. Поэтому стиль статического модуля, вероятно, более желателен, поскольку мы можем явно получить доступ к новому поведению. В противном случае поведение sprintf / printf будет обращаться к исходной строке ToString, а это не то, что мы хотим в соответствии с вопросом. Я считаю это ошибкой компилятора. Ошибка подана над здесь

Кстати: fsi pretty printing может облегчить это для сценариев только для FSI (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"