异步链接操作的类型不匹配错误 [英] type mismatch error for async chained operations

查看:61
本文介绍了异步链接操作的类型不匹配错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以前,对于我的问题,有一个非常紧凑而全面的答案.

Previously had a very compact and comprehensive answer for my question.

我让它适用于我的自定义类型,但是由于某种原因,我不得不将其更改为字符串类型,这现在导致类型不匹配错误.

I had it working for my custom type but now due to some reason I had to change it to string type which is now causing type mismatch errors.

module AsyncResult =
    let bind (binder : 'a -> Async<Result<'b, 'c>>) (asyncFun : Async<Result<'a, 'c>>) : Async<Result<'b, 'c>> =
        async {
            let! result = asyncFun
            match result with
            | Error e -> return Error e
            | Ok x -> return! binder x
        }

    let compose (f : 'a -> Async<Result<'b, 'e>>) (g : 'b -> Async<Result<'c, 'e>>) = fun x -> bind g (f x)
    let (>>=) a f = bind f a
    let (>=>) f g = compose f g

面向铁路的功能

let create (json: string) : Async<Result<string, Error>> =
    let url = "http://api.example.com"
    let request = WebRequest.CreateHttp(Uri url)
    request.Method <- "GET"

     async {
         try
             // http call
             return Ok "result"
         with :? WebException as e -> 
             return Error {Code = 500; Message = "Internal Server Error"}
     }

测试

类型不匹配错误

let chain = create
         >> AsyncResult.bind (fun (result: string) -> (async {return Ok "more results"}))


match chain "initial data" |> Async.RunSynchronously with
 | Ok data -> Assert.IsTrue(true)
 | Error error -> Assert.IsTrue(false)

错误详细信息:

EntityTests.fs(101,25):[FS0001]类型不匹配.期待'(string-> string-> Async< Result< string,Error>>)->'a',但给出了'Async< Result<'b,'c>>->Async< Result<'d,'c>>'类型'string->字符串->与Async< Result< string,Error>>>>'类型不匹配.

EntityTests.fs(101, 25): [FS0001] Type mismatch. Expecting a '(string -> string -> Async<Result<string,Error>>) -> 'a' but given a 'Async<Result<'b,'c>> -> Async<Result<'d,'c>>' The type 'string -> string -> Async<Result<string,Error>>' does not match the type 'Async<Result<'a,'b>>'.

EntityTests.fs(101,25):[FS0001]类型不匹配.期待'(string-> string-> Async< Result< string,Error>>)->'a',但给出了'Async< Result< string,'b>>->异步< Result< string,'b>>'类型'string->字符串->异步< Result< string,Error>>>>'与类型'Async< Result< string,'a>>'.

EntityTests.fs(101, 25): [FS0001] Type mismatch. Expecting a '(string -> string -> Async<Result<string,Error>>) -> 'a' but given a 'Async<Result<string,'b>> -> Async<Result<string,'b>>' The type 'string -> string -> Async<Result<string,Error>>' does not match the type 'Async<Result<string,'a>>'.

修改

临时或部分申请

在上面的示例中,咖喱函数是否存在问题?例如,如果create函数具有此签名.

In context of above example, is it the problem with curried functions? for instance if create function has this signature.

let create (token: string) (json: string) : Async<Result<string, Error>> =

然后使用咖喱函数构建链

and then later build chain with curried function

let chain = create "token" >> AsyncResult.bind (fun (result: string) -> (async {return Ok "more results"}))


编辑2 以下情况有问题吗?

签名

let create (token: Token) (entityName: string) (entityType: string) (publicationId: string) : Async<Result<string, Error>> =

测试

let chain = create token >> AsyncResult.bind ( fun (result: string) -> async {return Ok "more results"} )

match chain "test" "article" "pubid" |> Async.RunSynchronously with

推荐答案

更新:在答案的最前面,即使您的编辑2更改了所有内容.

Update: At the front of the answer, even, since your edit 2 changes everything.

在您的编辑2中,您最终揭示了您的 actual 代码,而您的问题非常简单:您误解了类型在咖喱F#函数中的工作方式.

In your edit 2, you have finally revealed your actual code, and your problem is very simple: you're misunderstanding how the types work in a curried F# function.

当您的 create 函数看起来像 let create(json:string)= ... 时,它是一个参数的函数.它获取了一个字符串,并返回了一个结果类型(在这种情况下,为 Async< Result< string,Error>>> ).因此,函数签名为 string->.异步< Result< string,Error>>> .

When your create function looked like let create (json: string) = ..., it was a function of one parameter. It took a string, and returned a result type (in this case, Async<Result<string, Error>>). So the function signature was string -> Async<Result<string, Error>>.

但是您刚刚向我们展示的 create 函数完全是另一种类型.它需要四个参数(一个 Token 和三个字符串),而不是一个.这意味着它的签名是:

But the create function you've just shown us is a different type entirely. It takes four parameters (one Token and three strings), not one. That means its signature is:

Token -> string -> string -> string -> Async<Result<string, Error>>

记住 currying 的工作原理:多个参数的任何功能都可以看作是一系列一个参数的函数,该函数返回该链中的下一个"函数.例如, let add3 a b c = a + b + c 的类型为 int->int->int->int ;这意味着 add3 1 返回的函数等效于 let add2 b c = 1 + b + c .依此类推.

Remember how currying works: any function of multiple parameters can be thought of as a series of functions of one parameter, which return the "next" function in that chain. E.g., let add3 a b c = a + b + c is of type int -> int -> int -> int; this means that add3 1 returns a function that's equivalent to let add2 b c = 1 + b + c. And so on.

现在,紧记一下,看看您的函数类型.如您在示例中所做的那样,当您将单个Token值传递给它(称为 create token 时,您会得到一个类型为

Now, keeping currying in mind, look at your function type. When you pass a single Token value to it as you do in your example (where it's called as create token, you get a function of type:

string -> string -> string -> Async<Result<string, Error>>

这是一个带有字符串的函数,该函数返回另一个带有字符串的函数,该函数带一个字符串,它返回一个第三函数,该函数带有一个字符串.字符串并返回 Async< Result< whatever>> .现在,将其与 bind 函数中的 binder 参数的类型进行比较:

This is a function that takes a string, which returns another function that takes a string, which returns a third function which takes a string and returns an Async<Result<whatever>>. Now compare that to the type of the binder parameter in your bind function:

(binder : 'a -> Async<Result<'b, 'c>>)

在这里,'a string ,所以'b 是,'c 错误.因此,当将通用 bind 函数应用于您的特定情况时,它正在寻找类型为 string->的函数.异步< Result<'b,'c>> .但是您给它提供了 string->类型的功能.字符串->字符串->异步< Result< string,Error>>> .这两种功能类型不同

Here, 'a is string, so is 'b, and 'c is Error. So when the generic bind function is applied to your specific case, it's looking for a function of type string -> Async<Result<'b, 'c>>. But you're giving it a function of type string -> string -> string -> Async<Result<string, Error>>. Those two function types are not the same!

这是造成类型错误的根本原因.您正在尝试将函数返回一个函数,该函数返回一个将返回X类型结果的函数的设计模式( bind 设计模式)期望为返回X类型结果的函数.您需要的是称为 apply 的设计模式.我必须尽快离开,以便没有时间给您写有关如何使用 apply 的说明,但幸运的是, apply 的详细信息.这就是问题的原因:需要使用 apply 时,您使用了 bind .

That's the fundamental cause of your type error. You're trying to apply a function that returns a function that returns function that returns a result of type X to a design pattern (the bind design pattern) that expects a function that returns a result of type X. What you need is the design pattern called apply. I have to leave quite soon so I don't have time to write you an explanation of how to use apply, but fortunately Scott Wlaschin has already written a good one. It covers a lot, not just "apply", but you'll find the details about apply in there as well. And that's the cause of your problem: you used bind when you needed to use apply.

原始答案如下:

我还不知道是什么原因导致了您的问题,但我有一个怀疑.但是首先,我想评论一下 AsyncResult.bind 的参数名称错误.这是你写的:

I don't yet know for a fact what's causing your problem, but I have a suspicion. But first, I want to comment that the parameter names for your AsyncResult.bind are wrong. Here's what you wrote:

    let bind (binder : 'a -> Async<Result<'b, 'c>>)
             (asyncFun : Async<Result<'a, 'c>>) : Async<Result<'b, 'c>> =

(我将第二个参数移到第一个参数的行上,因此它不会在Stack Overflow的较小列大小上滚动,但是如果类型正确,则可以正确编译:由于这两个参数是垂直排列的,因此F#会知道它们都属于同一个父级",在这种情况下为函数.)

(I moved the second parameter in line with the first parameter so it wouldn't scroll on Stack Overflow's smallish column size, but that would compile correctly if the types were right: since the two parameters are lined up vertically, F# would know that they are both belonging to the same "parent", in this case a function.)

查看第二个参数.您已将其命名为 asyncFun ,但其类型描述中没有箭头.那不是一个函数,而是一个价值.函数看起来像 something->somethingElse .您应将其命名为 asyncValue ,而不是 asyncFun .通过将其命名为 asyncFun ,您可以为以后的混乱做好准备.

Look at your second parameter. You've named it asyncFun, but there's no arrow in its type description. That's not a function, it's a value. A function would look like something -> somethingElse. You should name it something like asyncValue, not asyncFun. By naming it asyncFun, you're setting yourself up for confusion later.

现在为您提出的问题提供答案.我认为您的问题是这条线,您已经违反了 F#越位规则" :

Now for the answer to the question you asked. I think your problem is this line, where you've fallen afoul of the F# "offside rule":

let chain = create
         >> AsyncResult.bind (fun (result: string) -> (async {return Ok "more results"}))

请注意>> 运算符的位置,该运算符位于其第一个操作数的左侧.是的,在大多数情况下,F#语法似乎允许这样做,但是我怀疑如果您仅将函数定义更改为以下内容,则代码将起作用:

Note the position of the >> operator, which is to the left of its first operand. Yes, the F# syntax appears to allow that in most situations, but I suspect that if you simply change that function definition to the following, your code will work:

let chain =
    create
 >> AsyncResult.bind (fun (result: string) -> (async {return Ok "more results"}))

或者更好,因为这是一种很好的风格使 |> (和>> )运算符与它们的第一个操作数对齐:

Or, better yet because it's good style to make the |> (and >>) operators line up with their first operand:

let chain =
    create
    >> AsyncResult.bind (fun (result: string) -> (async {return Ok "more results"}))

如果您仔细观察Scott Wlaschin在 https://fsharpforfunandprofit.com中制定的规则,/posts/fsharp-syntax/,您会注意到,他的示例显示了越位规则"的例外,他这样写:

If you look carefully at the rules that Scott Wlaschin lays out in https://fsharpforfunandprofit.com/posts/fsharp-syntax/, you'll note that his examples where he shows exceptions to the "offside rule", he writes them like this:

let f g h =   g   // defines a new line at col 15
           >> h   // ">>" allowed to be outside the line

请注意,>> 字符如何仍仍位于函数定义中 = 的右侧.我不知道F#规范对函数定义和越位规则的组合怎么说(Scott Wlaschin很好,但是他不是规范,所以他可能是错的,而且我没有时间查找规范现在),但是当我编写的函数定义与函数在同一行中,而其余部分在下一行中编写时,我发现它所做的事情我没想到.

Note how the >> character is still to the right of the = in the function definition. I don't know exactly what the F# spec says about the combination of function definitions and the offside rule (Scott Wlaschin is great, but he's not the spec so he could be wrong, and I don't have time to look up the spec right now), but I've seen it do funny things that I didn't quite expect when I wrote functions with part of the function definition on the same line as the function, and the rest on the next line.

例如,我曾经写过像这样的东西,但是没用:

E.g., I once wrote something like this, which didn't work:

let f a = if a = 0 then
        printfn "Zero"
    else
        printfn "Non-zero"

但是后来我将其更改为这个,它确实起作用了:

But then I changed it to this, which did work:

let f a =
    if a = 0 then
        printfn "Zero"
    else
        printfn "Non-zero"

我注意到在Snapshot的答案中,他使您的 chain 函数定义在一行上,并且对他有用.所以我怀疑那是你的问题.

I notice that in Snapshot's answer, he made your chain function be defined on a single line, and that worked for him. So I suspect that that's your problem.

经验法则:如果您的函数在同一行的 = 之后有任何内容,请将函数全部放在一行上.如果您的函数将是两行,则在 = 之后不加任何内容.例如:

Rule of thumb: If your function has anything after the = on the same line, make the function all on one line. If your function is going to be two lines, put nothing after the =. E.g.:

let f a b = a + b  // This is fine
let g c d =
    c * d  // This is also fine
let h x y = x
          + y  // This is asking for trouble

这篇关于异步链接操作的类型不匹配错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆