2015-10-25 62 views
4

我一直在嘗試使用免費monads創建一個HTTP客戶端,類似於RúnarBjarnason給出的討論中採用的方法Composable application architecture with reasonably priced monads在自由代數中進行流式傳輸的最佳方式是什麼?

我到目前爲止的內容可以在這段代碼中看到,https://bitbucket.org/snippets/atlassian-marketplace/EEk4X

它工作正常,但我並不完全滿意。最大的難題在於需要將HttpOps參數化爲嵌入代數,以便允許流式傳輸請求和響應主體。這使得它不可能建立你的代數簡單地說

​​

如果你嘗試,你從編譯器的illegal cyclic reference錯誤。爲了解決這個問題,你可以使用的情況下,類

type App0[A] = Coproduct[InteractOps, HttpcOps[App, ?], A] 
case class App[A](app0: App0[A]) 

這解決了循環引用問題,但引入了一個新的問題。我們不再有Inject[InteractOps, App]實例可用,這意味着我們不再有Interact[App]Httpc[HttpcOps[App, ?]]實例,因此我們必須爲manually define them代數。對於這樣一個小而簡單的代數,這並不太繁重,但對於更大的東西,它可以變成很多樣板。

是否有另一種方法可以讓我們以更方便的方式包括流構成代數?

回答

3

我不知道我明白爲什麼App需要引用自己。什麼是例如這種預期的含義:

App(inj(Send(
    Request(GET, uri, v, hs, 
      StreamT(App(inj(Send(
      Request(GET, uri, v, hs, 
        StreamT(App(inj(Tell("foo"))))))))))))) 

即,通過嵌套任意深度的HTTP請求的流來產生一個EntityBody罐出於某種原因。這似乎是比你需要的更多的權力。

下面是一個免費的單子使用流的一個簡單的例子:

case class Ask[A](k: String => A) 
case class Req[F[_],A](k: Process[Ask, String] => A) 
type AppF[A] = Coproduct[Ask, Req[Ask,?], A] 
type App[A] = Free[AppF, A] 

在這裏,每個Req給你的String個流(scalaz.stream.Process)。字符串是通過詢問下一個字符串來產生的(例如,通過從標準輸入或HTTP服務器讀取或者其他)。但請注意,流的暫停函數不是App,因爲我們不希望Req有機會生成其他Req

+0

想讓'App'成爲自我引用的原因是允許流式傳輸不同的效果。部分請求主體可能來自數據庫,部分可能來自文件,部分可能來自不同的數據庫。我也不相信從其他請求組成請求過於強大。設想一個實用程序來連接S3上的存儲桶中的文件並將它們放入另一個存儲桶中。 – purefn

+0

我認爲你應該在monad上設置參數化流,monad可以是或不是應用monad。允許它們獨立地變化繞過你的遞歸問題,並允許你限制允許你的流服務的操作。 – Apocalisp

+0

這不僅僅是因爲你允許請求由其他請求組成。您允許服務器要求您執行任意數量的其他請求(或響應!),以便在響應中查看任何給定的字節向量。 限制是你的朋友。 http://blog.higher-order.com/blog/2014/12/21/maximally-powerful/ – Apocalisp

相關問題