我想知道,为什么WAI的Aplication
类型被设计为Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
。为什么不Request -> (Response -> IO ()) -> IO ()
?文档说
一种特殊数据类型,用于指示 WAI 处理程序已收到响应。这是为了避免在应用程序的定义中需要 Rank2Type。
强烈建议只有 WAI 处理程序导入并使用此数据类型的数据构造函数。
这种特殊的数据类型如何帮助避免Rank2Types
?
Application
真的应该是
type Application = Request -> (forall b. (Response -> IO b) -> IO b)
-- a.k.a. Request -> Codensity IO Response
-- newtype Codensity f a = Codensity { runCodensity :: forall b. (a -> f b) -> f b }
-- which is closely related to continuations
也就是说,Application
取一个Request
,一个函数f
,它用一个Response
调用f
。
app :: Application
app req f = f _resp
Application
迫使你打电话给f
。除了f
之外,没有其他函数能够在app
内部产生IO b
,所以,由于app
必须产生IO b
,app
必须调用f
。wai
取决于调用f
app
,它可以用这个Application
强制执行它。但是,Application
排名较高,这有些不可取。
实际使用的解决方案是:
type Application = Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
-- a.k.a. Request -> ContT ResponseReceived IO Response
-- in general, ContT is "bigger" and "weaker" than Codensity
-- but, in exchange, does not require higher ranked types
app :: Application
app req f = f _resp
f
仍然是app
手头唯一可以产生ResponseReceived
的东西,只要app
不违反它与wai
的契约并导入构造函数。您不能将ResponseReceived
替换为()
.每个人和他们的母亲都有构造函数(()
(()
可用。我认为甚至不可能没有它。这使得它不适合Application
:
type Application = Request -> (Response -> IO ()) -> IO ()
app :: Application
app _ _ = return () -- uh oh