4

我一直在尝试使用 Free monads 创建 HTTP 客户端,类似于 Rúnar Bjarnason 在演讲中采用的方法,即价格合理的 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]andHttpc[HttpcOps[App, ?]]实例,因此我们必须为我们的代数手动定义它们。对于像这样的小而简单的代数,这并不太繁重,但对于更大的东西,它可以变成很多样板。

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

4

1 回答 1

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都给你一个 s 的流 ( scalaz.stream.Process) String。这些字符串是通过询问下一个字符串来生成的(例如,通过从标准输入或 HTTP 服务器或其他任何内容中读取)。但请注意,流的暂停函子不是App,因为我们不想给 a生成其他sReq的机会。Req

于 2015-10-27T18:50:34.693 回答