gpt4 book ai didi

f# - 异步链式操作的类型不匹配错误

转载 作者:行者123 更新时间:2023-12-01 04:38:11 25 4
gpt4 key购买 nike

以前对我的question有一个非常紧凑而全面的答案。
我可以将其用于自定义类型,但是由于某种原因,我不得不将其更改为字符串类型,这现在导致类型不匹配错误。

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"}
}

测试
AsyncResult.bind行的类型不匹配错误
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 -> string -> Async<Result<string,Error>>'与类型 'Async<Result<'a,'b>>'不匹配。
EntityTests.fs(101,25):[FS0001]类型不匹配。预期为 '(string -> string -> Async<Result<string,Error>>) -> 'a'但给定为 'Async<Result<string,'b>> -> Async<Result<string,'b>>'类型 'string -> string -> Async<Result<string,Error>>'与类型 'Async<Result<string,'a>>'不匹配。

编辑
咖喱或部分申请
在以上示例的上下文中,咖喱函数是否存在问题?例如,如果create函数具有此签名。
let create (token: string) (json: string) : Async<Result<string, Error>> =

然后用咖喱函数建立链
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更改了所有内容。

在您的编辑2中,您终于揭示了您的实际代码,并且您的问题非常简单:您误解了类型在咖喱F#函数中的工作方式。

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

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

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等效的函数。等等。

现在,紧记一下,看看您的函数类型。当像在示例中一样将单个Token值传递给它时(称为 create token时),您将得到一个类型的函数:

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


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

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


在这里, 'astring'b也是, 'cError。因此,当通用 bind函数应用于您的特定情况时,它正在寻找类型 string -> Async<Result<'b, 'c>>的函数。但是,您赋予它 string -> string -> string -> Async<Result<string, Error>>类型的功能。这两种功能类型不一样!

这是造成类型错误的根本原因。您试图将一个函数返回给设计模式( bind设计模式),该函数将返回类型X的结果的函数返回到期望函数返回类型X的结果。您需要的是设计模式称为 apply。我必须尽快离开,以便没有时间给您写有关如何使用 apply的解释,但幸运的是, Scott Wlaschin has already written a good one。它涵盖了很多内容,而不仅仅是“应用”,而且您还将在其中找到有关 apply的详细信息。这就是造成问题的原因:需要使用 bind时使用了 apply

原始答案如下:

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

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


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

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

现在为您提出的问题的答案。我认为您的问题是这行,您已经违反了 F# "offside rule"

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


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

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


还是更好,因为 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/中列出的规则,您会注意到他的示例显示了“越位规则”的例外,他这样写:

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


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

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

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


但后来我将其更改为此方法,它确实起作用了:

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


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

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

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

关于f# - 异步链式操作的类型不匹配错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49586488/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com