Validation F#使用匹配验证参数

Validation F#使用匹配验证参数,validation,parameters,f#,match,Validation,Parameters,F#,Match,我在学法语。我想知道验证输入参数的最佳实践。在我的天真中,我曾认为我可以做这样的事情: let foo = match bar with | <test for valid> -> bar | _ -> "invalid" [<Struct>] type Result<'T,'TError> = /// Represents an OK or a Successful result. The code succeeded with

我在学法语。我想知道验证输入参数的最佳实践。在我的天真中,我曾认为我可以做这样的事情:

let foo = match bar with
| <test for valid> -> bar
| _ -> "invalid"
[<Struct>] 
type Result<'T,'TError> =  
    /// Represents an OK or a Successful result. The code succeeded with a value of 'T. 
    | Ok of ResultValue:'T  
    /// Represents an Error or a Failure. The code failed with a value of 'TError representing what went wrong. 
    | Error of ErrorValue:'TError
let foo=将条形图与
|->bar
|->“无效”
当然,由于类型不匹配,这不起作用。所以我想看看有经验的F#程序员在这方面使用的模式。比赛?如果/那么/否则


还有什么?

用F#表示无效状态的基本方法是使用
选项
类型,它有两个可能的值
None
表示无效状态,
Some()
表示有效值

因此,在您的情况下,您可以编写如下内容:

let foo = 
  match bar with
  | <test for valid> -> Some(bar)
  | _ -> None

你可以这样做

type Bar =
    | Bar of string
    | Foo of int

let (|IsValidStr|_|) x = if x = Bar "bar" then Some x else None
let (|IsValidInt|_|) x = if x = Foo 0 then Some x else None

let foo (bar:Bar) = 
    match bar with
    | IsValidStr x -> Some x
    | IsValidInt x -> Some x
    | _ -> None

也就是说,您可以使用活动模式来检查实际的业务规则,并根据OP在注释中写的内容返回选项实例:

您可以在发布Fyodor的帖子中定义一个类型,它捕获了两种可能的结果:

type Result<'TSuccess,'TFailure> = 
    | Success of 'TSuccess
    | Failure of 'TFailure
使用时,请再次使用匹配:

let myInput = "NotABool"
match checkBool myInput with
| Success b -> printfn "I'm happy: %O" b
| Failure f -> printfn "Did not like because: %s" f
如果您只想继续使用有效bool,则代码只能在无效参数上失败,因此您可以执行以下操作:

let myValidBool = 
    match checkBool myInput with
    | Success b -> b
    | Failure f -> failwithf "I did not like the args because: %s" f

您遇到了问题,因为您试图将一个值绑定到两种可能的类型,这取决于程序流,这与静态类型不兼容

如果我有一些值
foo
,它不能是,例如,一个
字符串
或一个
int
,这取决于程序流;它必须在编译时解析为一种类型

但是,您可以使用可以在单个类型中表示多个不同选项的

下面是实现这一点的方法的摘要


结果类型/其中一个

F#4.1目前通过提供,介绍了
结果
类型。在其他语言中,您可能会发现这种类型称为

let tryParseFloat str =
   match System.Double.TryParse str with
   |  true, f -> Some f
   | _ -> None
定义如下:

let foo = match bar with
| <test for valid> -> bar
| _ -> "invalid"
[<Struct>] 
type Result<'T,'TError> =  
    /// Represents an OK or a Successful result. The code succeeded with a value of 'T. 
    | Ok of ResultValue:'T  
    /// Represents an Error or a Failure. The code failed with a value of 'TError representing what went wrong. 
    | Error of ErrorValue:'TError
您可以决定成功或失败:

match tryParseFloat "0.0001" with
|Ok v -> // handle success
|Error err -> // handle error
match tryParseFloat "0.0001" with
|Some value -> // handle success
|None -> // handle error
在我看来,这是首选选项,尤其是在内置类型的F#4.1+中。这是因为它允许您包含有关某些活动失败的方式和原因的信息


选项类型/可能

选项
类型包含
Some'T
或简单的
None
。选项类型用于指示值的存在或不存在,
None
填充的角色类似于其他语言中的
null
,但要安全得多

您可能会发现这种类型在其他语言中被称为
,可能是

let tryParseFloat str =
   match System.Double.TryParse str with
   |  true, f -> Some f
   | _ -> None
您可以决定成功或失败:

match tryParseFloat "0.0001" with
|Ok v -> // handle success
|Error err -> // handle error
match tryParseFloat "0.0001" with
|Some value -> // handle success
|None -> // handle error

作文

在这两种情况下,您都可以分别使用
选项
结果
模块中关联的
映射
绑定
功能轻松组合选项或结果:

地图:

绑定:

tryparsefflot“0.001”|>Result.bind(fun x->trySqrt x);;
val it:结果=正常0.0316227766
tryparsefflot“-10.0”|>Result.bind(乐趣x->trySqrt x);;
val it:Result=错误“负数的sqrt为虚值”
tryparsefflot“皮卡德的长笛”|>Result.bind(乐趣x->trySqrt x);;
val it:结果=
错误“提供的字符串(Picard的长笛)不是有效的浮点”

请注意,在这两种情况下,尽管链接了多个操作,我们仍返回一个结果或选项,这意味着通过遵循这些模式,您只需在所有验证完成后检查一次结果

这避免了嵌套的
if
语句或
match
语句的潜在可读性噩梦

阅读这篇文章的好地方是前面提到的那篇文章

例外情况


最后,您可以选择抛出异常,以防止某些值被验证。如果您预计会发生这种情况,那么这绝对不是首选,但如果事件确实异常,那么这可能是最佳选择。

什么是
bar
?模式匹配通常优先于if/then/else选中此项:绝对
match
-它比
if/then/else
提供了更大的灵活性,但在编写检查时也提供了更多防止错误的措施。我在a中写了几个例子:类型测试,匹配参数Hanks Anton,实际上你下面的例子更接近我的想法:谢谢Anton,实际上你下面的例子更接近我的想法:
let(| Bool |)str=match System.Boolean.TryParse(str)with |(true,Bool)->Some(bool)|->None let(| String |)(o:obj)=将o与|::匹配?字符串为s->Some(s)| uu->None
,尽管这让我需要单独处理None来发出错误消息。当所需结果为整数,但错误路径为字符串时,您如何调整解决方案?您没有使用定义的
Bar
类型。你是想从比赛中把它还回来吗?这都是很棒的东西!非常感谢上一个示例中的想法,如果myInput是bool,我如何访问它?它现在是否包装在结果类型中?通过使用
Success
模式,如最后一个片段所示:
b
绑定到包装的bool。啊,是的,所以假设我正在处理cli args,argv,例如,我想检查第一个arg是否有有效的bool字符串(真/假),将其转换为实际布尔值,并将其指定给新名称以供以后使用。我能用火柴一次性完成吗?e、 g.为了修改您的示例,我可以说:let myBool=更新了答案。我无法安装Fsharp 4.1:PM>安装包Fsharp.Core试图解析依赖项'System.ValueTuple'(≥ 4.3.0)'. 安装程序包:“System.ValueTuple 4.3.0”程序包需要NuGet客户端版本“2.12”或更高版本,但当前NuGet版本为“2”。
val bind: binder:('T -> 'U option) -> option:'T option -> 'U option
val bind: binder:('T -> Result<'U, 'TError>) -> result:Result<'T, 'TError> -> Result<'U, 'TError>
let trySqrt x =
   if x < 0.0 then Error "sqrt of negative number is imaginary"
   else Ok (sqrt x)
tryParseFloat "0.001" |> Result.bind (fun x -> trySqrt x);;
val it : Result<float,string> = Ok 0.0316227766

tryParseFloat "-10.0" |> Result.bind (fun x -> trySqrt x);;
val it : Result<float,string> = Error "sqrt of negative number is imaginary"

tryParseFloat "Picard's Flute" |> Result.bind (fun x -> trySqrt x);;
val it : Result<float,string> =
  Error "Supplied string (Picard's Flute) is not a valid float"