[英]Function composition and type annotations in Haskell
总菜鸟在这里。 我已经看到了使用do
块执行HTTP Get请求的示例,但是我想通过组合来完成。
像这样:
get url = getResponseBody . simpleHTTP $ getRequest url
合适吗 对于此功能,正确的类型注释是什么?
可以的
get :: String -> IO String
get url = getResponseBody =<< simpleHTTP (getRequest url)
但是我想组成而不是绑定。 有什么更好/正确的方法?
如果我们查看类型:
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组合器。 出于各种数学原因,但为简单起见,仅将其视为函数组合的功能不足以仅将函数组合在一起。 为了合成动作,引入了monad,它们带有自己的合成运算符。 比较这两个合成运算符的类型:
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> (a -> m c) -- Imported from Control.Monad
它们非常相似,不是吗? 唯一的区别是将m
与Monad
约束相加。 这是monad 的合成运算符,它比普通的更具限制性和强大.
运算符,这意味着它可以执行更多操作,例如排序和执行IO操作,但也意味着它可以使用更少的类型。 可以用作
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
函数包装在Kleisli
包装器中,并用runKleisli
对其进行runKleisli
。 您也可以跳过Control.Category
而只需使用Control.Arrow
:
get = runKleisli (Kleisli getResponseBody <<< Kleisli simpleHTTP) <<< getRequest
您甚至可以将其交换为
get = getRequest >>> runKleisli (Kleisli simpleHTTP >>> Kleisli getResponseBody)
如果您希望它从左到右而不是从右到左阅读。
最后,有几种方法可以编写等效的代码,但是,如果您坚持使用无点样式(不必惯用),那么大多数人最熟悉和可读的方法就是使用通常的代码.
和<=<
因为这是最短的实现,并且使用了更多可识别的运算符。 在构建自定义内容时, Control.Category
和Control.Arrow
非常有用,但是对于内置类型,通常已经定义了更有意义的运算符。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.