В Haskell do
-обозначение используется для цепочки вещей, похожих на операторы. Оператор - это конструктор типа, подобный IO
, применяемый к определенному типу результата. Например, оператор system "ls"
имеет тип IO ExitCode
.
Другие конструкторы типов, кроме IO
, могут работать как операторы. Все, что требуется для do
-notation, - это то, что конструктор типов реализует интерфейс Monad
, который объясняет, как разумно объединять операторы в цепочку. do
-блок, допускается только один тип заявления ! Это должны быть все операторы IO
или все операторы ActionT Text IO
. В вашем примере вы смешиваете два, что вызывает ошибку. Функция Скотти get
ожидает оператор ActionT Text IO
:
get :: RoutePattern -> ActionM () -> ScottyM ()
-- ActionM is actually a synonym for ActionT Text IO
Хорошая новость заключается в том, что существует способ преобразования (обычный термин на Haskell - это "lift ") IO
операторы в ActionT Text IO
операторы. Последние на самом деле являются своего рода «декоратором» (обычный термин на Haskell - «монадный преобразователь» ) над IO
действиями, которые обеспечивают дополнительную функциональность, связанную со Скотти. Вы можете «поднять» IO
действия в декоратор, используя функцию liftIO
, например:
get "/url" $ do
liftIO (system "ls")
text "Success"
В общем, когда мы можем использовать liftIO
, чтобы поднятьобычное IO
утверждение в «украшенное» утверждение? Конструктор типа «decorator» должен иметь экземпляр MonadIO
помимо обычного Monad
экземпляра. MonadIO
- это то, что обеспечивает функцию liftIO
.
В нашем случае, рассмотрим доступные экземпляры для ActionT
:
(MonadIO m, ScottyError e) => MonadIO (ActionT e m)
Что означает что-тонапример, "если m
имеет экземпляр MonadIO
, как это обычно делает IO
, а тип ошибки e
имеет экземпляр ScottyError
, как это делает Text
, то мы можем поднять операторы IO
доActionT e m
операторов ".
А специализированный тип для liftIO
:
liftIO :: IO a -> ActionT Text IO a