簡體   English   中英

在F#中組成異步計算

[英]Composing async computations in F#

我正在編寫一個異步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>)

而且,如您所見,我用自己的擴展擴展了異步模塊:

module Async =
    let map f m =
        async {
            let! v = m
            return f v
        }

    let flatMap f m =
        async {
            let! v = m
            return! f v
        }

我試圖實現的目標是構建一個模塊,該模塊具有可以在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 }

當然,如果需要出於其他目的下載數據以用於其他目的,則可以將代碼分為downloadDatagetJson

通常,在編寫功能代碼時,您有兩個選擇來組成計算:

  • 使用語言的語法(如循環, lettry .. withuse兩個簡單的F#和異步工作流)。 如果您正在編寫一些計算,則此方法通常效果很好,因為該語言旨在描述計算並且可以很好地做到這一點。

  • 使用自定義組合器(例如map>>|>或特定於庫的運算符)。 如果您要建模的內容不只是計算,則需要這樣做,例如交互式動畫,股票期權,用戶界面組件或解析器。 但是,只有在語言的基本功能不足以表達問題時,我才會遵循這種方式。

另外,您可能對F#數據庫感興趣,該數據庫為各種任務實現了幫助程序,包括HTTP請求JSON解析JSON類型提供程序 ,這可能會使您的生活更加輕松。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM