我有一个F#区分联盟,我想在其中将一些“构造函数逻辑”应用于构造联盟案例中使用的任何值。假设联合看起来像这样:
type ValidValue =
| ValidInt of int
| ValidString of string
// other cases, etc.
现在,我想对实际传入的值应用一些逻辑,以确保它们有效。为了确保我最终不会处理实际上不是有效的ValidValue
实例(尚未使用验证逻辑构造过的实例),我将构造函数设为私有并公开了一个强制执行构造它们的逻辑。
type ValidValue =
private
| ValidInt of int
| ValidString of string
module ValidValue =
let createInt value =
if value > 0 // Here's some validation logic
then Ok <| ValidInt value
else Error "Integer values must be positive"
let createString value =
if value |> String.length > 0 // More validation logic
then Ok <| ValidString value
else Error "String values must not be empty"
此方法有效,使我可以强制执行验证逻辑,并确保ValidValue
的每个实例均有效。但是,问题在于,该模块之外的任何人都无法在ValidValue
上进行模式匹配以检查结果,从而限制了“受歧视联盟”的用处。
我希望允许外部用户像其他任何DU一样继续进行模式匹配并使用ValidValue
,但是如果它具有私有构造函数,那是不可能的。我能想到的唯一解决方案是使用私有构造函数将DU中的每个值包装为单例联合类型,并将实际的ValidValue
构造函数公开。这会将案例暴露给外部,从而使它们能够与之匹配,但仍主要防止外部调用者构造它们,因为实例化每个案例所需的值将具有私有构造函数:
type VInt = private VInt of int
type VString = private VString of string
type ValidValue =
| ValidInt of VInt
| ValidString of VString
module ValidValue =
let createInt value =
if value > 0 // Here's some validation logic
then Ok <| ValidInt (VInt value)
else Error "Integer values must be positive"
let createString value =
if value |> String.length > 0 // More validation logic
then Ok <| ValidString (VString value)
else Error "String values must not be empty"
现在,调用方可以匹配ValidValue
的大小写,但是他们无法读取联合大小写内部的实际整数和字符串值,因为它们被包装在具有私有构造函数的类型中。可以使用每种类型的value
函数来解决此问题:
module VInt =
let value (VInt i) = i
module VString =
let value (VString s) = s
不幸的是,现在增加了呼叫者的负担:
// Example Caller
let result = ValidValue.createInt 3
match result with
| Ok validValue ->
match validValue with
| ValidInt vi ->
let i = vi |> VInt.value // Caller always needs this extra line
printfn "Int: %d" i
| ValidString vs ->
let s = vs |> VString.value // Can't use the value directly
printfn "String: %s" s
| Error error ->
printfn "Invalid: %s" error
是否有更好的方法来强制执行我一开始想要的构造函数逻辑,而又不会增加其他地方的负担?
答案 0 :(得分:7)
您可以有专用的构造函数,但是要公开具有相同名称的公共活动模式。这是定义和使用它们的方式(为简便起见,省略了创建函数):
import support_functions
def main_menu():
print('Fechamento de Caixa')
numero = input('Numero: ')
dados = [numero]
dados = dados.__add__(menu_a())
dados = dados.__add__(menu_b())
dados = dados.__add__(menu_c())
print('(D) Cartões')
print(' (D-a) POS')
dados = dados.__add__(['(D) Cartões', '(D-a) POS'])
dados = dados.__add__(menu_d_a_a())
dados = dados.__add__(menu_d_a_b())
dados = dados.__add__(menu_d_a_c())
dados = dados.__add__(menu_d_a_d())
dados = dados.__add__(menu_e())
dados = dados.__add__(menu_f())
dados = dados.__add__(menu_g())
dados = dados.__add__(menu_h())
dados = dados.__add__(menu_i())
dados = dados.__add__(menu_j())
dados = dados.__add__(menu_k())
dados = dados.__add__(menu_l())
dados = dados.__add__(menu_m())
dados = dados.__add__(menu_n())
return dados
def menu_a():
a = support_functions.convert_and_replace(input('(A) Valor em Dinheiro: R$'))
return ['(A) Valor em Dinheiro', a]
def menu_b():
b = support_functions.convert_and_replace(input('(B) Valor total de Sangrias: R$'))
return ['(B) Valor total de Sangrias', b]
def menu_c():
print('(C) Cheques')
c_a = support_functions.convert_and_replace(input(' (C-a) Valor de Cheques à Vista: R$'))
c_b = support_functions.convert_and_replace(input(' (C-b) Valor de Cheques à Prazo: R$'))
c = c_a + c_b
return ['(C) Cheques', '(C-a) Valor de Cheques à Vista', c_a, '(C-b) Valor de Cheques à Prazo', c_b,
'# Total (C)', c]
def menu_d_a_a():
print(' (D-a-a) Crédito')
visa_cr = support_functions.convert_and_replace(input(' Visa: R$'))
mastercard_cr = support_functions.convert_and_replace(input(' Mastercard: R$'))
elo_cr = support_functions.convert_and_replace(input(' Elo: R$'))
banricompras_30 = support_functions.convert_and_replace(input(' Banricompras 30 Dias: R$'))
banricompras_45 = support_functions.convert_and_replace(input(' Banricompras 45 Dias: R$'))
banricompras_60 = support_functions.convert_and_replace(input(' Banricompras 60 Dias: R$'))
banricompras_90 = support_functions.convert_and_replace(input(' Banricompras 90 Dias: R$'))
amex = support_functions.convert_and_replace(input(' American Express: R$'))
cabal_cr = support_functions.convert_and_replace(input(' Cabal: R$'))
# diners_cr = support_functions.convert_and_replace(input(' Diners: R$'))
hiper_cr = support_functions.convert_and_replace(input(' Hipercard: R$'))
verdecard_cr = support_functions.convert_and_replace(input(' Verdecard: R$'))
# Totalizador Credito
d_a_a = visa_cr + mastercard_cr + elo_cr + banricompras_30 + banricompras_45 + banricompras_60 + banricompras_90 \
+ cabal_cr + amex + hiper_cr + verdecard_cr
return ['(D-a-a) Crédito', 'Visa', visa_cr, 'Mastercard', mastercard_cr, 'Elo', elo_cr, 'Banricompras 30 Dias',
banricompras_30, 'Banricompras 45 Dias', banricompras_45, 'Banricompras 60 Dias', banricompras_60,
'Banricompras 90 Dias', banricompras_90, 'American Express', amex, 'Cabal', cabal_cr, 'Hipercard',
hiper_cr, 'Verdecard', verdecard_cr, '# Total (D-a-a)', d_a_a]
def menu_d_a_b():
print(' (D-a-b) Débito')
banricompras_db = support_functions.convert_and_replace(input(' Banricompras: R$'))
cabal_db = support_functions.convert_and_replace(input(' Cabal: R$'))
elo_db = support_functions.convert_and_replace(input(' Elo: R$'))
mastercard_db = support_functions.convert_and_replace(input(' Mastercard: R$'))
visa_db = support_functions.convert_and_replace(input(' Visa: R$'))
# Totalizador Debito
d_a_b = banricompras_db + cabal_db + elo_db + mastercard_db + visa_db
return ['(D-a-b) Débito', 'Banricompras', banricompras_db, 'Cabal', cabal_db, 'Elo', elo_db, 'Mastercard',
mastercard_db, 'Visa', visa_db, '# Total (D-a-b)', d_a_b]
def menu_d_a_c():
print(' (D-a-c) Parcelados')
elo_pc = support_functions.convert_and_replace(input(' Elo: R$'))
hiper_pc = support_functions.convert_and_replace(input(' Hipercard: R$'))
mastercard_pc = support_functions.convert_and_replace(input(' Mastercard: R$'))
verdecard_pc = support_functions.convert_and_replace(input(' Verdecard: R$'))
visa_pc = support_functions.convert_and_replace(input(' Visa: R$'))
# Totalizador Parcelado
d_a_c = elo_pc + hiper_pc + mastercard_pc + verdecard_pc + visa_pc
return ['(D-a-c) Parcelados', 'Elo', elo_pc, 'Hipercard', hiper_pc, 'Mastercard', mastercard_pc, 'Verdecard',
verdecard_pc, 'Visa', visa_pc, '# Total (D-a-c)', d_a_c]
def menu_d_a_d():
print(' (D-a-d) Vale/Beneficio')
alelo_al = support_functions.convert_and_replace(input(' Alelo Alimentação: R$'))
alelo_rf = support_functions.convert_and_replace(input(' Alelo Refeição: R$'))
banricard_al = support_functions.convert_and_replace(input(' Banricard Alimentação: R$'))
banricard_cb = support_functions.convert_and_replace(input(' Banricard Combustivel: R$'))
sodexo_al = support_functions.convert_and_replace(input(' Sodexo Alimentação: R$'))
sodexo_cb = support_functions.convert_and_replace(input(' Sodexo Combustivel: R$'))
vr_al = support_functions.convert_and_replace(input(' VR Alimentação: R$'))
vr_bn = support_functions.convert_and_replace(input(' VR Beneficio: R$'))
vr_cb = support_functions.convert_and_replace(input(' VR Combustivel: R$'))
vr_rf = support_functions.convert_and_replace(input(' VR Refeição: R$'))
ticket_al = support_functions.convert_and_replace(input(' Ticket Alimentação: R$'))
ticket_rt = support_functions.convert_and_replace(input(' Ticket Restaurante: R$'))
shell_rf = support_functions.convert_and_replace(input(' Shell Resgate Fácil: R$'))
# Totalizador Vale/Beneficio
d_a_d = alelo_al + alelo_rf + banricard_al + banricard_cb + sodexo_al + sodexo_cb + vr_al + vr_bn + vr_cb + vr_rf + ticket_al + ticket_rt + shell_rf
return ['(D-a-d) Vale/Beneficio', 'Alelo Alimentação', alelo_al, 'Alelo Refeição', alelo_rf,
'Banricard Alimentação', banricard_al, 'Banricard Combustivel', banricard_cb, 'Sodexo Alimentação',
sodexo_al, 'Sodexo Combustivel', sodexo_cb, 'VR Alimentação', vr_al, 'VR Beneficio', vr_bn,
'VR Combustivel', vr_cb, 'VR Refeição', vr_rf, 'Ticket Alimentação', ticket_al, 'Ticket Restaurante',
ticket_rt, 'Shell Resgate Fácil', shell_rf, '# Total (D_a_d', d_a_d]
def menu_e():
e = support_functions.convert_and_replace(input('(E) Notas à Prazo: R$'))
return ['(E) Notas à Prazo', e]
def menu_f():
f = support_functions.convert_and_replace(input('(F) Recarga: R$'))
return ['(F) Recarga', f]
def menu_g():
g = support_functions.convert_and_replace(input('(G) Quitação de Notas à Prazo: R$'))
return ['(G) Quitação de Notas à Prazo', g]
def menu_h():
h = support_functions.convert_and_replace(input('(H) Pagamento de Notas Fiscais: R$'))
return ['(H) Pagamento de Notas Fiscais', h]
def menu_i():
i: int = input('(I) Leitura Café: ')
return ['(I) Leitura Café', i]
def menu_j():
j: int = input('(J) Lanches Entrada: ')
return ['(J) Lanches Entrada', j]
def menu_k():
k: int = input('(K) Lanches Saida: ')
return ['(K) Lanches Saida', k]
def menu_l():
l = support_functions.convert_and_replace(input('(L) Vales Troco Recebidos: R$'))
return ['(L) Vales Troco Recebidos', l]
def menu_m():
m = support_functions.convert_and_replace(input('(M) Vales Troco Emitidos: R$'))
return ['(M) Vales Troco Emitidos', m]
def menu_n():
n = support_functions.convert_and_replace(input('(N) Troco Final: R$'))
return ['(N) Troco Final', n]
答案 1 :(得分:2)
除非有特殊的原因要求使用区分式联合,否则鉴于您提供的特定用例,这听起来好像根本就不需要区分式联合,因为活动模式会更有用。例如:
let (|ValidInt|ValidString|Invalid|) (value:obj) =
match value with
| :? int as x -> if x > 0 then ValidInt x else Invalid
| :? string as x -> if x.Length > 0 then ValidString x else Invalid
| _ -> Invalid
此时,呼叫者可以匹配并确保已应用该逻辑。
match someValue with
| ValidInt x -> // ...
| _ -> // ...