Mis*_*hor 2 .net f# asynchronous functional-programming computation-expression
我正在编写异步HTTP API客户端模块/库.为了使所有内容尽可能干,我试图从进行API调用的单独部分组成每个HTTP API调用,自下而上:构建请求,获取响应,将响应读入字符串缓冲区,解析JSON内容那个字符串缓冲区成一个对象.
到目前为止,我有这个代码:
module ApiUtils =
// ... request builder fns omitted ...
let getResponse<'a> (request : Net.WebRequest) =
request.AsyncGetResponse()
let readResponse (response : Net.WebResponse) =
use reader = new StreamReader(response.GetResponseStream())
reader.AsyncReadToEnd()
let getString = getResponse >> (Async.flatMap readResponse)
let parseJson<'T> responseText : 'T =
Json.JsonConvert.DeserializeObject<'T> responseText
let getJson<'T> = getString >> (Async.map parseJson<'T>)
Run Code Online (Sandbox Code Playgroud)
而且,正如您所看到的,我已经使用我自己的附加功能扩展了Async模块:
module Async =
let map f m =
async {
let! v = m
return f v
}
let flatMap f m =
async {
let! v = m
return! f v
}
Run Code Online (Sandbox Code Playgroud)
我想要实现的目标是构建一个具有我可以在async块中使用的函数的模块,以充分利用计算表达式语法.我想知道我是否做得对,如果我选择了正确的名字,等等.我几乎没有正式的函数式编程教育,有时候我甚至不确定我知道自己在做什么.
在F#中编写异步代码时,我发现使用内置计算异步工作流语法更容易,而不是尝试构建更复杂的组合器.
在您的示例中,如果您只编写一个简单的函数,则不会真正复制任何代码,因此我认为以下内容不会破坏DRY原则并且它相当简单(并且扩展代码以处理异常也很容易,否则会很困难):
let getJson<'T> (request:Net.WebRequest) = async {
let! response = request.AsyncGetResponse()
use reader = new StreamReader(response.GetResponseStream())
let! data = reader.AsyncReadToEnd()
return Json.JsonConvert.DeserializeObject<'T> data }
Run Code Online (Sandbox Code Playgroud)
当然,您可以将代码拆分为downloadData,getJson如果您需要在代码中的其他位置下载数据以用于其他目的.
通常,在编写功能代码时,您有两种选择如何组合计算:
使用的语言的语法(如循环,let并且try .. with,use在这两个滑动F#和在异步工作流).如果您正在编写一些计算,这种方法通常很有效,因为该语言旨在描述计算并且可以很好地完成.
使用自定义组合器(例如map,>>和/ |>或特定于库的运算符).如果您要对不仅仅是计算的东西进行建模,例如交互式动画,股票期权,用户界面组件或解析器,则需要这样做.但是,如果语言的基本功能不足以表达问题,我只会遵循这条路径.
除此之外,您可能对F#数据库感兴趣,它为各种任务实现帮助程序,包括HTTP请求,JSON解析和JSON类型提供程序,这可能会让您的生活更加轻松.