Можете ли вы инкапсулировать многозначные союзы?

Я вижу, что вы можете принудительно использовать конструктор для единичных дискриминационных союзов, можете ли вы сделать то же самое с несколькими случаями?

например

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)
Другие вопросы по тегам