.net 为什么签名会是-&燃气轮机;结果<;字符*字符串>;?
我关注Scott的,他带来的一个例子是:.net 为什么签名会是-&燃气轮机;结果<;字符*字符串>;?,.net,f#,.net,F#,我关注Scott的,他带来的一个例子是: type Result<'a> = | Success of 'a | Failure of string let pchar (charToMatch, input) = if System.String.IsNullOrEmpty (input) then Failure "no input!" else let first = input.[0] if
type Result<'a> =
| Success of 'a
| Failure of string
let pchar (charToMatch, input) =
if System.String.IsNullOrEmpty (input) then
Failure "no input!"
else
let first = input.[0]
if first = charToMatch then
let remaining = input.[1..]
Success (charToMatch, remaining)
else
let msg = sprintf "exepecting '%c' got '%c'" charToMatch first
Failure msg
类型ResultResult
:
返回类型应该是
ResultResult@kagetoki从用法推断类型是正确的
而且,这看起来像一个解析器组合器库。pchar
的目的是尝试从输入中提取字符。如果成功返回字符和字符串的其余部分,则表示该字符串的自然方式是char*string
。这被包装在结果
中以支持失败,因此最终返回类型应该是:结果
这里的想法是,您可以使用结果
类型包装任何操作的结果,以便您可以用业务逻辑对控制流进行建模。这在“业务错误”和“异常”之间存在重叠的场景中尤其有用。在您的示例中,字符串一次解析一个字符,函数将返回解析后的字符以及字符串的其余部分(作为F#元组)
Result
是泛型类型,因此它可以表示任何操作的结果。如果函数未返回结果,则类型的'a
类型参数的值将与函数的返回类型相同。考虑一个简单的例子如下:
设f()=('t','est”)
编译器告诉我们这有类型签名unit->char*string
。这是因为该函数始终采用单元
(empty/void)参数并返回单个字符和字符串的元组。如果我们稍微更改该函数以返回结果
,如下所示:
让f()=('t',“est”)|>Success
现在编译器告诉我们签名是unit->Result
。与函数的原始返回类型相同,包装在Result
类型中。您的示例与此相同,只是返回的字符和字符串元组基于输入参数,并且如果输入参数为null或空字符串,函数可能返回失败
如果您定义了一个bind
函数来“展开”结果并将其中的值传递给另一个函数f
,则可以轻松地处理结果类型:
let bind f = function
| Success s -> f s
| Failure f -> Failure f
使用bind
,您可以轻松地将pchar
调用的结果传递给使用该值的另一个函数,而该函数不必显式地接受result
类型:
let printChar (c, str) =
printfn "Matched Char %A, Remaining String: '%s'" c str
pchar ('a', "aaab") |> bind (printChar >> Success)
我经常使用这种模式,但我在结果
类型中添加了第二个通用参数,它表示函数的事件
或效果
。这增加了Result
类型的实用性,允许我们在代码中处理特定的失败
事件(而不是检查特定字符串),或者在成功的结果中包含域事件(如警告)或副作用
为此,我使用了名为OperationResult
的类型,以避免与内置的F#Result
类型混淆。由于我有时会包括事件/效果成功结果和失败,因此我为SuccessfulResult
定义了一个类型,即返回值或返回值以及事件列表。总的来说,这提供了以下类型来定义操作结果
:
/// Represents the successful result of an Operation that also yields events
/// such as warnings, informational messages, or other domain events
[<Struct>]
type SuccessfulResultWithEvents<'result,'event> =
{
Value: 'result
Events: 'event list
}
/// Represents the successful result of an Operation,
/// which can be either a value or a value with a list of events
[<Struct>]
type SuccessfulResult<'result,'event> =
| Value of ResultValue: 'result
| WithEvents of ResultWithEvents: SuccessfulResultWithEvents<'result,'event>
member this.Result =
match this with
| Value value -> value
| WithEvents withEvents -> withEvents.Value
member this.Events =
match this with
| Value _ -> []
| WithEvents withEvents -> withEvents.Events
/// Represents the result of a completed operation,
/// which can be either a Success or a Failure
[<Struct>]
type OperationResult<'result,'event> =
| Success of Result: SuccessfulResult<'result,'event>
| Failure of ErrorList: 'event list
///表示同时产生事件的操作的成功结果
///例如警告、信息性消息或其他域事件
[]
在Events中键入SuccessfulResults=
{
值:'结果
事件:事件列表
}
///表示操作的成功结果,
///可以是值,也可以是包含事件列表的值
[]
键入成功结果=
|结果值Value:'结果
|带有ResultWithEvents的事件:SuccessfulResultWithEvents
请记住这个结果=
与此匹配
|价值->价值
|WithEvents WithEvents->WithEvents.Value
各位议员,我谨此陈辞=
与此匹配
|值->[]
|WithEvents WithEvents->WithEvents.Events
///表示已完成操作的结果,
///这可能是成功,也可能是失败
[]
类型操作结果=
|成功的结果:成功的结果
|错误列表失败:“事件列表”
如果您想使用它,我将所有这些都打包在(和)上的一个库中,以及一个名为operation
的计算表达式。GitHub页面也有一些示例。但是失败的不是char*string,而是string@AlexGordon这是正确的,但是这里的故障
类型不是泛型的。在您的结果
类型中,只有成功案例是泛型,失败
始终是字符串,因此它不会影响泛型类型签名。非常感谢。我的头脑即将爆炸,思考这个成功的问题——我不明白它怎么可能隐式地知道如何将一个元组转换为一个元组Result@AlexGordon它不会转换元组。把Success
想象成一个通用构造函数,它接受任何类型,'a
,并返回一个结果!很好的解释,谢谢。这个怎么样<代码>让绑定f=function | Success s->f s | Failure f->Failure f
?我们将一些参数f
传递给bind,会发生什么?它如何知道是去成功分支还是失败分支?它如何与这两个匹配?关于unit->Result
的另一个问题是返回值为Result
的原因,因为该值将是Success的超集