在您的免费代数中进行流式传输的最佳方式是什么?
What's the best way to do streaming in your free algebra?
我一直在尝试使用 Free monad 创建 HTTP 客户端,类似于 Rúnar Bjarnason 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
错误。为了解决这个问题,你可以使用 case class
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 来进行代数计算。对于像这样的小而简单的代数,这并不太繁重,但对于更大的东西,它可能会变成很多样板。
是否有另一种方法可以让我们以更方便的方式包含流 和 组合代数?
我不确定我是否理解为什么 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
都会为您提供一个 String
的流 (scalaz.stream.Process
)。字符串是通过询问下一个字符串的内容生成的(例如,通过从标准输入或 HTTP 服务器或其他任何地方读取)。但请注意,流的暂停仿函数不是 App
,因为我们不想给 Req
生成其他 Req
的机会。
我一直在尝试使用 Free monad 创建 HTTP 客户端,类似于 Rúnar Bjarnason 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
错误。为了解决这个问题,你可以使用 case class
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 来进行代数计算。对于像这样的小而简单的代数,这并不太繁重,但对于更大的东西,它可能会变成很多样板。
是否有另一种方法可以让我们以更方便的方式包含流 和 组合代数?
我不确定我是否理解为什么 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
都会为您提供一个 String
的流 (scalaz.stream.Process
)。字符串是通过询问下一个字符串的内容生成的(例如,通过从标准输入或 HTTP 服务器或其他任何地方读取)。但请注意,流的暂停仿函数不是 App
,因为我们不想给 Req
生成其他 Req
的机会。