Промежуточное программное обеспечение для данных по запросу - PullRequest
0 голосов
/ 15 октября 2018

В 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))))

В этом промежуточном программном обеспечении я проверяю, есть ли у меня значение, отличное от нуля, в my-header в :headers, еслида, я приложу некоторые данные к карте request.Это свидетельствует о том, что я могу трактовать request и response как несколько "данные с состоянием".

Я все еще новичок в haskell и хотел сделать подобные вещи с scotty.Посмотрев на тип middleware , я могу создать промежуточное программное обеспечение следующим образом:

myMiddleware :: Middleware 
myMiddleware app req respond = app req respond

После долгого изучения типа я все еще не знаю, как это сделать.Это.Некоторое чтение и размышления заставляют меня предположить, что это невозможно, Middleware может только закорачивать обработчик и / или изменять сгенерированный ответ.Это правда?


1 Ответ

0 голосов
/ 15 октября 2018

Это меня тоже смутило надолго!Но выяснение этого дало мне полезную технику для понимания типов библиотек 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
...