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



我一直在尝试使用Free monad创建一个HTTP客户端,类似于Rúnar Bjarnason演讲中所采用的方法,使用价格合理的monad的可组合应用程序架构。

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

它可以工作,但我不完全满意。最大的痛点是需要将HttpOps参数化到它将嵌入的代数上,以便允许请求和响应体的流化。这使得通过简单地说

来构建代数是不可能的。
type App[A] = Coproduct[InteractOps, HttpcOps[App, ?], A]

如果你尝试,你会从编译器得到一个illegal cyclic reference错误。要解决这个问题,可以使用case类

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

这解决了循环引用问题,但引入了一个新问题。我们不再有现成的Inject[InteractOps, App]实例,这意味着我们不再有Interact[App]Httpc[HttpcOps[App, ?]]实例,所以我们必须为我们的代数手动定义它们。对于像这样的小而简单的代数,这并不太麻烦,但对于更大的东西,它可能会变成很多样板文件。

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

我不确定我理解为什么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请求流生成。这似乎比你需要的更强大。

下面是一个在自由单子中使用流的简单例子:

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

最新更新