aut*_*322 6 haskell clojure haskell-wai
在clojure,我可以写这样的东西:
(defn wrap-my-header
[handler]
(fn [request]
(let [request (if (get-in request [:headers "my-header"])
(assoc request :has-my-header? true)
request)]
(handler request))))
Run Code Online (Sandbox Code Playgroud)
在这个中间件,我检查,如果我在有一个非空值my-header的:headers,如果是,我将在附加一些数据request映射.这表明我可以对待request并response作为一种有点"有状态"的数据.
我仍然是哈斯克尔的新人,并且想做类似的事情scotty.在查看中间件的类型后,我可以创建这样的中间件:
myMiddleware :: Middleware
myMiddleware app req respond = app req respond
Run Code Online (Sandbox Code Playgroud)
在长时间盯着这个类型后,我仍然不知道该怎么做.一些阅读和思考让我认为这是不可能的,Middleware只能使处理程序短路和/或改变生成的响应.这是真的?
这让我困惑了很久!但弄清楚它给了我一个有用的技术来理解Haskell库类型.
首先,我将从我的中间件未定义开始:
myMiddleware :: Middleware
myMiddleware = undefined
Run Code Online (Sandbox Code Playgroud)
type Middleware = Application -> Application
Run Code Online (Sandbox Code Playgroud)
让我们从第一层(或抽象层)开始,让中间件获取一个Application并返回一个Application.我们不知道如何修改应用程序,因此我们将准确地返回现在传递的内容.
myMiddleware :: Application -> Application
myMiddleware theOriginalApp = theOriginalApp
Run Code Online (Sandbox Code Playgroud)
但什么是应用程序?再次,让我们转向Hackage:
type Application = Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
Run Code Online (Sandbox Code Playgroud)
应用程序是一个功能!我们可能不知道每个部分应该做什么或做什么,但我们可以找到.让Application我们的类型签名替换为函数类型:
myMiddleware :: (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
-> (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
myMiddleware theOriginalApp = theOriginalApp
Run Code Online (Sandbox Code Playgroud)
现在我们可以看到这种类型应该允许我们访问Request!但是我们如何使用它呢?
我们可以theOriginalApp将函数定义扩展为与返回类型匹配的lambda表达式:
myMiddleware :: (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
-> (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
myMiddleware theOriginalApp = (\req sendResponse -> undefined)
Run Code Online (Sandbox Code Playgroud)
我们现在可以随意使用我们想做的任何事情:
myMiddleware :: (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
-> (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
myMiddleware theOriginalApp = (\req sendResponse ->
let myModifiedRequest = addSomeHeadersIfMissing req in
undefined)
Run Code Online (Sandbox Code Playgroud)
那怎么样undefined?好吧,我们试图将我们的lambda与返回函数的类型相匹配,该函数接受一个Request和一个函数(我们不关心)并返回一个IO ResponseReceived.
所以,我们需要一些可以使用myModifiedRequest和返回的东西IO ResponseReceived.幸运的是我们的类型签名表明它theOriginalApp的类型正确!为了使它适合,我们只需要赋予它sendResponse功能.
myMiddleware :: (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
-> (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
myMiddleware theOriginalApp = (\req sendResponse ->
let myModifiedRequest = addSomeHeadersIfMissing req in
theOriginalApp myModifiedRequest sendResponse)
Run Code Online (Sandbox Code Playgroud)
就是这样,那会有效!我们可以通过简化类型注释来简化可读性Middleware,并摆脱lambda.(我们也可以ETA-减少和去除sendResponse从参数和定义足月,但我认为它更清晰,如果它停留.)
结果:
myMiddleware :: Middleware
myMiddleware theOriginalApp req sendResponse =
let myModifiedRequest = addSomeHeadersIfMissing req in
theOriginalApp myModifiedRequest sendResponse
Run Code Online (Sandbox Code Playgroud)