У меня была парочка мелких приложений на Хаскеле. Работали они в продакшене, и вполне успешно.Но тут в датацентре случился переезд, пожар, ремонт - в общем пришлось заново собирать эти приложеньица из сорцов. Я думал что делов-то на 15 минут, а не тут-то было.
Версии пакетов на хакадже давно уехали вперед, даже сигнатуры экспортируемых из пакетов функций поменялись - отличненький маленький cabal hell. Stackage же на тот момент когда я всё это писал еще не существовал. И самое неприятное, с чем я столкнулся - это изменения в Web Application Interface. Я использовал вторую версию WAI, где у приложения была вот такая сигнатура:
Application :: IO Response
А в третьей версии, которая нынче используется везде, стала вот такая:
Appliaction :: Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
Так как внутри приложения происходит перекладывение из базы в очередь, из формочек в базу, из мемкеша еще куда-нибудь - кругом IO, то, естественно валом полезли ошибки несовпадения типов:
Couldn't match type ‘IO b0’ with ‘Response’
Expected type: IO BL.ByteString
-> (BL.ByteString -> IO b0) -> Response
Actual type: IO BL.ByteString
-> (BL.ByteString -> IO b0) -> IO b0
Спасибо каналу ruHaskell в гиттере, там мне объяснили что теперь интерфейс warp имеет continuation-passing style.
Если раньше приложение WAI выглядело как-то так:
import...
application :: Config -> Application
application conf req = do
case pathInfo req of
[] -> yay
["something"] -> process conf req
--yay :: ResponseReceived
yay = responseBuilder status404 [("Content-type", "text/plain")] (copyByteString "yay, 404")
То теперь вы должны передать ниже продолжение, которым собственно и должно закончиться ваше действие:
import...
...
application :: Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
application request respond =
case pathInfo request of
["io"] -> ioaction request respond
_ -> respond deny
deny :: Response
deny = responseLBS status403 [("Content-Type", "text/plain")] "Nothing to see here, citizen. Move along."
ioaction :: Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
ioaction request respond = do
let userId = fromMaybe (Just "") $ lookup "userid" (queryString request) :: Maybe BS.ByteString
let id = fromMaybe "" userId
syncResponse <- sync id
respond $ responseLBS status200 [("Content-Type", "image/gif")] (B64.decode "R0lGODlhAQABAIAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==")
sync :: String -> IO BL.ByteString
sync id = do
request <- parseUrl "
http://oursyncserver.com/sync?u=" ++ id
response <- httpLbs request manager
return (responseBody response)
Наверное, я пишу банальные для опытных хаскелистов вещи, но я-то всего лишь любитель, и мне этот момент стоил дня головной боли. Меньше, чем конфликты между изменившимися версиями пакетов (про stackage напишу отдельно), но всё-таки достаточно много.