10

我好奇地看了一眼WAI界面,虽然它看起来很简单,但我惊讶地发现它需要多少次迭代才能稳定在当前形式!

我曾认为用于资源安全的 CPS 样式将是最有趣的事情,但看起来还有很多东西需要学习!

$ git log -p --reverse -- wai/Network/Wai.hs | grep '\+type Application'
+type Application = Request -> Iteratee B.ByteString IO Response
+type Application = Request -> ResourceT IO Response
+type Application = Request -> C.ResourceT IO Response
+type Application = Request -> IO Response
+type Application = Request -> (forall b. (Response -> IO b) -> IO b)
+type Application = Request -> (Response -> IO ResponseReceived)
                            -> IO ResponseReceived

一些考古得出的结果有些不尽人意:

$ git log --reverse -G 'type Application' --pretty=oneline -- wai/Network/Wai.hs | cat
879d4a23047c3585e1cba4cdd7c3e8fc13e17592 Moved everything to wai subfolder
360442ac74f7e79bb0e320110056b3f44e15107c Began moving wai/warp to conduit
af7d1a79cbcada0b18883bcc5e5e19a1cd06ae7b conduit 0.3
fe2032ad4c7435709ed79683acac3b91110bba04 Pass around an InternalState instead of living in ResourceT
63ad533299a0a5bad01a36171d98511fdf8d5821 Application uses bracket pattern
1e1b8c222cce96c3d58cd27318922c318642050d ResponseReceived, to avoid existential issues
4

1 回答 1

5

所有的设计似乎都受到三个主要问题的驱动:

  • 请求可以具有流式主体(因此我们不必在开始处理它们之前将它们全部加载到内存中)。如何最好地代表它?
  • 响应也可以流式传输。如何最好地代表它?
  • 如何确保在生成响应时分配的资源得到适当释放?(例如,如何确保在提供文件后释放文件句柄?)

type Application = Request -> Iteratee B.ByteString IO Response

这个版本使用 iteratees,这是 Haskell 中流数据的早期解决方案。Iteratee 消费者必须以“基于推送”的方式编写,这可能不如现代流媒体库中使用的“基于拉取”消费者自然。

请求的流式传输主体被提供给迭代对象,Response最后我们得到一个值。Response包含一个枚举器(一个将流式响应字节提供给服务器提供的响应迭代器的函数)。据推测,枚举器将使用诸如bracket.


type Application = Request -> ResourceT IO Response

此版本使用resourcet monad 转换器进行资源管理,而不是在枚举器中进行。Source两者都有一个特殊的类型RequestResponse它处理流数据(恕我直言,这有点难以理解)。


type Application = Request -> IO Response

此版本使用来自管道的流抽象,但避开了资源,而是提供了一个类似括号的responseSourceBracket函数来处理流响应中的资源。


type Application = Request -> (forall b. (Response -> IO b) -> IO b)
type Application = Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived

此版本转向基于延续的方法,该方法使处理函数能够使用类似常规bracket的函数来控制资源分配。回到第一方,在这方面!

导管不再用于流式传输。现在有一个用于读取请求正文块的普通Request -> IO ByteString 函数,以及一个用于生成响应流的(Builder -> IO ()) -> IO () -> IO () 函数。ResponseBuilder -> IO ()写入功能和刷新操作由服务器提供。)

与基于资源的版本一样,与基于迭代的版本不同,此实现允许您重叠读取请求正文和流式传输响应。

多态处理程序是一个巧妙的技巧,可确保Response -> IO b始终调用响应接收回调:处理程序需要返回 a b,而获得它的唯一方法是实际调用回调!

这种多态解决方案似乎引起了一些问题(也许将处理程序存储在容器中?)我们可以使用ResponseReceived没有公共构造函数的令牌,而不是使用多态。效果是一样的:处理程序代码获取它需要返回的令牌的唯一方法是调用回调。

于 2017-11-27T21:45:45.167 回答