Это меня тоже смутило надолго!Но выяснение этого дало мне полезную технику для понимания типов библиотек Haskell.
Сначала я начну с того, что мое промежуточное программное обеспечение не определено:
myMiddleware :: Middleware
myMiddleware = undefined
Так что же такое Middleware
?Ключ в том, чтобы взглянуть на определение типа :
type Middleware = Application -> Application
Давайте начнем с первого уровня (или уровня абстракции), когда промежуточное программное обеспечение принимает приложение и возвращает приложение,Мы не знаем, как изменить приложение, поэтому мы вернем именно то, что было передано на данный момент.
myMiddleware :: Application -> Application
myMiddleware theOriginalApp = theOriginalApp
Но что такое приложение?Снова, давайте перейдем к Hackage :
type Application = Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
Приложение - это функция!Мы можем не знать точно, что каждая часть должна делать или быть, но мы можем выяснить.Давайте заменим Application
в сигнатуре нашего типа на тип функции:
myMiddleware :: (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
-> (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
myMiddleware theOriginalApp = theOriginalApp
Теперь мы видим, что этот тип должен позволить нам получить доступ к Request
!Но как мы это используем?
Мы можем расширить theOriginalApp
в определении функции в лямбда-выражение, соответствующее типу возвращаемого значения:
myMiddleware :: (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
-> (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
myMiddleware theOriginalApp = (\req sendResponse -> undefined)
Мы можем сделать с запросом все, что захотим:
myMiddleware :: (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
-> (Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived)
myMiddleware theOriginalApp = (\req sendResponse ->
let myModifiedRequest = addSomeHeadersIfMissing req in
undefined)
А что насчет undefined
?Что ж, мы пытаемся сопоставить нашу лямбду с типом этой возвращаемой функции, которая принимает запрос и функцию (нас это не волнует) и возвращает 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)
И это все, что будет работать!Мы можем улучшить читаемость, упростив аннотацию типов до Middleware
и избавившись от лямбды.(Мы также можем eta-уменьшить и удалить термин sendResponse
как из аргументов, так и из определения, но я думаю, что будет понятнее, если он останется.)
Результат:
myMiddleware :: Middleware
myMiddleware theOriginalApp req sendResponse =
let myModifiedRequest = addSomeHeadersIfMissing req in
theOriginalApp myModifiedRequest sendResponse