sev*_*evo 10 haskell haskell-wai
我对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
Run Code Online (Sandbox Code Playgroud)
一些考古学产生了一些令人不满意的结果:
$ 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
Run Code Online (Sandbox Code Playgroud)
所有设计似乎都受到三个主要问题的驱动:
type Application = Request -> Iteratee B.ByteString IO Response
Run Code Online (Sandbox Code Playgroud)
这个版本使用迭代,这是Haskell中流数据的早期解决方案.Iteratee消费者必须以"基于推送"的方式编写,这可能比现代流媒体库中使用的"基于拉"的消费者更不自然.
请求的流体被提供给iteratee,最后得到一个Response值.它Response包含一个枚举器(一个将流式响应字节提供给服务器提供的响应迭代器的函数).据推测,枚举器将使用类似的函数来控制资源分配bracket.
type Application = Request -> ResourceT IO Response
Run Code Online (Sandbox Code Playgroud)
此版本使用resourcet monad转换器进行资源管理,而不是在枚举器中执行此操作.Source两者内部都有一种特殊类型Request,Response它处理流数据(这有点难以理解恕我直言).
type Application = Request -> IO Response
Run Code Online (Sandbox Code Playgroud)
此版本使用来自管道的流式抽象,但避开了资源,而是提供了一个类似括号的responseSourceBracket函数来处理流式响应中的资源.
type Application = Request -> (forall b. (Response -> IO b) -> IO b)
type Application = Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
Run Code Online (Sandbox Code Playgroud)
此版本转向基于延续的方法,该方法使处理程序函数能够使用类似常规bracket的函数来控制资源分配.在这方面回到原点!
管道不再用于流媒体.现在有一种简单的Request -> IO ByteString 功能,用于读取请求主体的块,和一个(Builder -> IO ()) -> IO () -> IO () 功能在Response用于产生响应流.(Builder -> IO ()写入功能和刷新操作由服务器提供.)
与基于资源的版本一样,与基于iteratee的版本不同,此实现允许您重叠读取请求主体并传输响应.
多态处理程序是一个巧妙的技巧,可确保Response -> IO b始终调用响应式回调:处理程序需要返回一个b,而获取一个的唯一方法是实际调用回调!
这种多态解决方案似乎引起了一些问题(可能是将容器存储在容器中?)而不是使用多态,我们可以使用ResponseReceived没有公共构造函数的令牌.效果是一样的:处理程序代码获取它需要返回的令牌的唯一方法是调用回调.