Можете ли вы инкапсулировать многозначные союзы?
Я вижу, что вы можете принудительно использовать конструктор для единичных дискриминационных союзов, можете ли вы сделать то же самое с несколькими случаями?
например
type MemberId =
| MemberId of int
| MemberGuid of Guid
Я сейчас пытаюсь в fsi
как это
val create : int -> T option
val create : Guid -> T option
но я предполагаю, что, как C#, F# не позволит вам перегрузить в зависимости от типа возвращаемого значения для развертки:
val value : T -> string
Редактировать ---------------
MemberId.fsi =
module MemberId
open System
type _T
val createId : int -> _T option
val createGuid : Guid -> _T option
val value : _T -> 'a
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
Кажется, довольно близко, но распаковщик не компилируется, и я не могу понять, как это написать
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'
| _ -> ()
3 ответа
Действительно, есть способ перегрузки с выходным параметром, используя несколько встроенных приемов:
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
Делая это, вы можете "перегружать" функции даже для выходных параметров.
В любом случае, большая разница с единичным DU заключается в том, что теперь распаковщик не является "безопасным", поэтому распаковщик имеет мало смысла, за исключением некоторых конкретных сценариев.
В этих случаях вы можете рассмотреть другие механизмы для разворачивания значений, такие как предоставление функций isX
или возврат параметров, которые могут быть дополнены активным шаблоном для развертывания.
Сказав это, если вы заинтересованы только в том, чтобы "скрыть" конструкторы для выполнения некоторых проверок, но не скрывать DU, вы можете просто скрыть конструкторы, вот пример:
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())
Функции не могут быть перегружены, но методы могут:
type MemberId =
private
| MemberId of int
| MemberGuid of Guid
static member create id = MemberId id
static member create guid = MemberGuid guid
Вот небольшой фрагмент кода из ответа Густаво, который мне нужен, который, кажется, работает сам по себе
module MemberId
open System
type MemberId =
| 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)