.net 为什么签名会是-&燃气轮机;结果<;字符*字符串>;?

.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

我关注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  first = charToMatch then
            let remaining = input.[1..]
            Success (charToMatch, remaining)
        else
            let msg = sprintf "exepecting '%c' got '%c'" charToMatch first
            Failure msg
类型ResultResult


返回类型应该是
Result
Result@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的超集