在免费代数中进行流式传输的最佳方法是什么?

时间:2015-10-25 19:33:57

标签: scala scalaz free-monad

我一直在尝试使用Free monads创建一个HTTP客户端,类似于RúnarBjarnason在Composable application architecture with reasonably priced monads给出的演讲中采用的方法。

到目前为止,我可以在此代码段中看到https://bitbucket.org/snippets/atlassian-marketplace/EEk4X

它运作正常,但我并不完全满意。最大的痛点是由于需要在嵌入的代数上参数化HttpOps,以便允许请求和响应主体的流式传输。这使得无法通过简单地说

来建立你的代数
type App[A] = Coproduct[InteractOps, HttpcOps[App, ?], A]

如果您尝试,则会从编译器中收到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代数。对于像这样的小而简单的代数,这不是太繁重,但对于更大的代数,它可以变成许多样板。

是否有另一种方法可以让我们以更方便的方式包含流组合代数?

1 个答案:

答案 0 :(得分:3)

我不确定我理解为什么App需要引用自己。什么是例如预期的含义:

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

也就是说,EntityBody由于某种原因可以由任意嵌套的HTTP请求流生成。这似乎比你需要的更强大。

以下是在免费monad中使用流的简单示例:

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都会为您提供scalaz.stream.Process的流(String)。通过向下一个字符串询问某些内容来生成字符串(例如,通过读取标准输入或HTTP服务器或其他内容)。但请注意,流的暂停仿函数不是App,因为我们不希望Req有机会生成其他Req