打印区分联合的案例标识符

时间:2017-05-15 13:46:53

标签: .net f# discriminated-union

我有一个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-identifier,即Some(丢弃“1”值)。我该怎么办?

3 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

你可以使用匹配:

public function viewUser(Request $request){

    $user = User::where('id', $request->id)->first();
    $langs = Language::all();
    $children = Child::where('user_id', $request->id)->get();
    $usrlang = DB::table('language_user')->where('user_id', $request->id)->get();
    $usrhob = DB::table('user_hobbies')->where('user_id', $request->id)->get();
    $userCh = [];
    $userLang = [];
    $userHobby = [];
    $chLang = [];
    $chHobby = [];

    foreach ($usrlang as $language){
        $userLang[] = $language;
    }
    foreach ($usrhob as $hobby){
        $userHobby[] = $hobby;
    }
    foreach ($children as $child){
        $userCh[] = $child;
        $languagesCh = DB::table('child_language')->where('child_id', $child->id)->get();
        $hobbiesCh = DB::table('child_language')->where('child_id', $child->id)->get();
        foreach ($languagesCh as $chL){
            $chLang[] = $chL;
        }
        foreach ($hobbiesCh as $chH){
            $chHobby[] = $chH;
        }
    }

    return view('detail', compact('user','langs', 'children', 'usrlang', 'usrhob', 'userCh', 'userLang', 'userHobby', 'chHobby', 'chLang'));
}

解决你的评论: 在这种情况下,我不会覆盖ToString方法,而是根据预期的行为进行单独的匹配。或者只是定义一个辅助函数来打印选项而不指定内容;

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

通过这种方式,您可以使用let printEmpty myOpt = match myOpt with | Some _ -> "Some" | None -> "None" 打印内容,也可以使用sprintf "%A" myOpt进行打印。

答案 2 :(得分:1)

对于它的价值,我将@ scrwtp的版本包含在“类型扩展”形式中,而不是Option&lt;'a&gt;的原始重新实现。理由只是最近定义的类型混淆了在ToString部分中使用caseLabel(原始问题中的Option&lt;'a&gt;定义类型)。

NB。因为我们不能从DU继承(它没有构造函数),所以我们不能重载ToString。因此,ToString的扩展仍然默认为原始的ToString。因此,静态模块样式可能更合适,因为我们可以显式访问新行为。否则,sprintf / printf的行为将访问原始的ToString,这不是我们想要的问题。我认为这是一个编译器错误。错误归档here

顺便说一下:FSI漂亮打印可以缓解仅适用于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"