Если мы посмотрим на типы:
getRequest :: String -> Request_String
simpleHTTP :: Request ty -> IO (Result (Response ty))
getResponseBody :: Result (Response ty) -> IO ty
type Request_String = Request String
Таким образом, для целей данного вопроса мы можем специализироваться типы для
simpleHTTP :: Request_String -> IO (Result (Response String))
getResponseBody :: Result (Response String) -> IO String
довольно ясно видеть, что мы можем просто сделать
simpleHTTP (getRequest url) :: IO (Result (Response String))
Однако, чтобы составить это с помощью getResponseBody
, мы должны использовать monadic combi nators. Для этого существуют всевозможные математические причины, но для простоты просто подумайте об этом, поскольку состав функций не достаточно мощный, чтобы составлять действия вместе, а только функции. Для составления действий были введены монады, и вместо этого они пришли со своими собственными операторами композиции. Сравните типы этих двух составных операторов:
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> (a -> m c) -- Imported from Control.Monad
Они очень похожи, не так ли? Единственная разница заключается в добавлении m
с ограничением Monad
. Это оператор композиции для монад, он более ограничительный и мощный, чем обычный оператор .
, что означает, что он может выполнять больше, например, последовательность и выполнение операций ввода-вывода, но это также означает, что он работает с меньшим количеством типов. Он может быть использован в качестве
get = simpleHTTP <=< simpleHTTP . getRequest
Однако, есть более обобщенный вариант композиции определяется в Control.Category
, который может обрабатывать как:
import Prelude hiding ((.), id) -- Hide the less general versions in Prelude
import Control.Category
import Control.Arrow
get = runKleisli (Kleisli getResponseBody . Kleisli simpleHTTP) . getRequest
Но это требует, чтобы вы обернуть монадических функции в Kleisli
Newtype обертки и разверните его с помощью runKleisli
. Вы также можете пропустить Control.Category
и просто использовать Control.Arrow
:
get = runKleisli (Kleisli getResponseBody <<< Kleisli simpleHTTP) <<< getRequest
Вы даже можете поменять это вокруг, чтобы быть
get = getRequest >>> runKleisli (Kleisli simpleHTTP >>> Kleisli getResponseBody)
, если вы хотите, чтобы читать слева направо, а не справа налево ,
В конце концов, существует несколько способов написания эквивалентного кода, но наиболее знакомый и читаемый большинству людей, если вы непреклоняетесь по беспроблемному стилю (необязательно быть идиоматическим), было бы использовать обычно .
и <=<
, поскольку это кратчайшая реализация и использует более узнаваемые операторы.Control.Category
и Control.Arrow
очень полезны при создании чего-то пользовательского, но для встроенных типов обычно уже определены определенные операторы, которые имеют больше смысла.
Можете ли вы предоставить типы для 'getResponseBody',' simpleHTTP' и 'getRequest'? Или, по крайней мере, модуль (ы), из которого они берутся? – bheklilr
Они происходят из Network.HTTP – Roman
Что случилось с привязкой? '= <<' _is_, в некотором смысле, композиция для монадических функций (также называемых стрелками Клейсли). Точнее, '<= <' - оператор композиции Клейсли: 'get = getResponseBody <=
leftaroundabout