您可以封装多区分大小写的工会吗? [英] Can you encapsulate multi case discriminated unions?

查看:52
本文介绍了您可以封装多区分大小写的工会吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我看到您可以强制使用构造函数单案例区分联合,多案例可以做到吗?

I see that you can enforce constructor usage of single-case discriminated unions, can you do the same with multi-case?

例如

type MemberId = 
  | MemberId of int
  | MemberGuid of Guid

我目前正在这样尝试fsi

val create : int -> T option
val create : Guid -> T option

但是我猜想像C#一样,F#不允许您根据展开类型的返回类型重载:

but I'm guessing like C#, F# won't allow you to overload based on return type for the unwrap:

val value : T -> string

编辑---------------

MemberId.fsi =

MemberId.fsi =

module MemberId
open System
type _T

val createId : int -> _T option
val createGuid : Guid -> _T option

val value : _T -> 'a

MemberId.fs =

MemberId.fs =

module MemberId
open System
type _T = 
    | Id of int
    | MemberGuid of Guid

let createId id = match id with
                | x when x>0 -> Some(Id(id))
                | _ -> None
let createGuid guid = Some(MemberGuid( guid))

let value (e:_T):int = e

似乎很接近,但是解包器无法编译,我似乎也无法弄清楚如何编写

Appears to be pretty close, but the unwrapper doesn't compile and I can't seem to figure out how to write it

TestConsumer MemberIdClient.fs =

TestConsumer MemberIdClient.fs =

module MemberIdClient
open System
open MemberId

let address1 = MemberId.create(-1)
let address2 = MemberId.create(Guid.Empty)

let unwrapped1 = 
  match address1 with
  | MemberId x -> () // compilation error on 'MemberId x'
  | _ -> ()

推荐答案

实际上,有一种方法可以使用一些内联技巧来重载输出参数:

Indeed there is a way to overload with the output parameter, using some inline tricks:

open System

type MemberId = 
    private
    | MemberId of int
    | MemberGuid of Guid

type Create = Create with
    static member ($) (Create, id  ) = MemberId id
    static member ($) (Create, guid) = MemberGuid guid

type Value = Value with
    static member ($) (Value, d:int ) = function MemberId   id   -> id   | _ -> failwith "Wrong case"
    static member ($) (Value, d:Guid) = function MemberGuid guid -> guid | _ -> failwith "Wrong case"

let inline create x : MemberId   = Create $ x
let inline value  x : 'IntOrGuid = (Value $ Unchecked.defaultof<'IntOrGuid>) x

let a = create 1
let b = create (Guid.NewGuid())

let c:int  = value a
let d:Guid = value b

通过这样做,即使在输出参数上,您也可以重载"功能.

By doing this you can 'overload' functions, even on output parameters.

无论如何,与单个案例DU的最大区别在于,现在的拆包器不是安全的",这就是为什么在某些特定情况下,拆包器毫无意义的原因.

Anyway the big difference with the single case DU is that now the unwrapper is not 'safe', that's why the unwrapper makes little sense, except in some specif scenarios.

在这些情况下,您可能会考虑其他解开值的机制,例如公开函数isX或返回可能与活动模式互补的选项来解开.

In these cases you may consider other mechanisms to unwrap the values, like exposing functions isX or returning options which may be complemented with an active pattern to unwrap.

话虽如此,如果您只想隐藏"构造函数以进行一些验证,而又不隐藏DU,则可以简单地隐藏构造函数,这是一个示例:

Having said that, if you are only interested in 'hiding' the constructors to do some validations, but not hiding the DU you can simply shadow the constructors, here's an example:

open System

type T = 
    | MemberId of int
    | MemberGuid of Guid

// Shadow constructors
let MemberId  x = if x > 0 then Some (MemberId x) else None
let MemberGuid x = Some (MemberGuid x)

let a = MemberId 1
let b = MemberGuid (Guid.NewGuid())
let c = MemberId -1

// but you can still pattern match
let printValue = function
| Some (MemberId   x) -> sprintf "case 1, value is %A" x
| Some (MemberGuid x) -> sprintf "case 2, value is %A" x
| None                -> "No value"

let ra = printValue a  // "case 1, value is 1"
let rb = printValue b  // "case 2, value is 67b36c20-2..."
let rc = printValue c  // "No value"

// and if you want to use an overloaded constructor
type T with
    static member Create id   = MemberId id
    static member Create guid = MemberGuid guid

let d = T.Create 1
let e = T.Create (Guid.NewGuid())

// or using the inline trick
type Create = Create with
    static member ($) (Create, id  ) = MemberId id
    static member ($) (Create, guid) = MemberGuid guid
let inline create x : T option = Create $ x

let d' = create 1
let e' = create (Guid.NewGuid())

这篇关于您可以封装多区分大小写的工会吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆