haskell – 与API交谈的最佳实践

我正在尝试在
Haskell中为API创建一些绑定.我注意到一些函数有大量的参数,例如

myApiFunction :: Key -> Account -> Int -> String -> Int -> Int -> IO (MyType)

拥有这么多论点本身并不一定是坏事.但作为一个用户,我不喜欢长的参数函数.但是,这些args中的每一个绝对是100%必需的.

是否有更多的haskell-ish方法来抽象这些函数的公共部分?此处的所有过去帐户都用于构建URL,因此我需要它可用,它所代表的内容完全取决于功能.但是某些事情是一致的,比如Key和Account,我想知道在这些论点上最好的是什么.

谢谢!

最佳答案 您可以将这些组合成更具描述性的数据类型:

data Config = Config
    { cKey :: Key
    , cAccount :: Account
    }

然后可能有类型或新类型,以使其他参数更具描述性:

-- I have no idea what these actually should be, I'm just making up something
type Count = Int
type Name = String
type Position = (Int, Int)

myApiFunction :: Config -> Count -> Name -> Position -> IO MyType
myApiFunction conf count name (x, y) =
    myPreviousApiFunction (cKey conf)
                          (cAccount conf)
                          name
                          name
                          x
                          y

如果总是需要配置,那么我建议你在一个阅读器monad中工作,你可以很容易地做到这一点

myApiFunction
    :: (MonadReader Config io, MonadIO io)
    => Count -> Name -> Position
    -> io MyType
myApiFunction count name (x, y) = do
    conf <- ask
    liftIO $myPreviousApiFunction
                (cKey conf)
                (cAccount conf)
                name
                name
                x
                y

这使用mtl库作为monad变换器.如果您不想一遍又一遍地键入该约束,您还可以使用ConstraintKinds扩展来对其进行别名:

{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
...

type ApiCtx io = (MonadReader Config io, MonadIO io)

...

myApiFunction
    :: ApiCtx io
    => Count -> Location -> Position
    -> io MyType
myApiFunction ...

根据您的具体应用,您还可以将其拆分为多个功能.我之前见过很多API

withCount :: ApiCtx io => Count    -> io a -> io a
withName  :: ApiCtx io => Name     -> io a -> io a
withPos   :: ApiCtx io => Position -> io a -> io a

(&) :: a -> (a -> b) -> b

request :: ApiCtx io => io MyType 
> :set +m   -- Multi-line input
> let r = request & withCount 1
|                 & withName "foo"
|                 & withPos (1, 2)
> runReaderT r (Config key acct)

这些只是少数技术,还有其他技术,但在此之后它们通常会变得更加复杂.对于如何做到这一点,其他人会有不同的偏好,我相信很多人会不同意我们是否其中一些甚至是好的做法(特别是ConstraintKinds,它并未被普遍接受).

如果你发现自己的类型签名太大了,即使应用了其中一些技术,那么也许你正在从错误的方向接近问题,也许这些功能可以分解为更简单的中间步骤,也许是一些这些参数可以逻辑地组合成更具体的数据类型,也许你只需要一个更大的记录结构来处理设置复杂的操作.它现在很开放.

点赞