异步链接操作的类型不匹配错误 [英] type mismatch error for async chained operations
问题描述
以前,对于我的问题,有一个非常紧凑而全面的答案.
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屋!