我试图在Haskell中为API创建一些绑定。我注意到一些函数有很多参数,例如
myApiFunction :: Key -> Account -> Int -> String -> Int -> Int -> IO (MyType)
本身并不一定要有这么多论点。但作为一个用户,我不喜欢长期的论证功能。但是,这些算法中的每一个都是绝对必要的。
是否有更多的haskell-ish方法来抽象这些函数的公共部分?此处的所有过去帐户都用于构建URL,因此我需要它可用,它所代表的内容完全取决于功能。但是,某些事情是一致的,例如Key
和Account
,我想知道最好对这些论点进行抽象是什么。
谢谢!
答案 0 :(得分:2)
您可以将这些组合成更具描述性的数据类型:
data Config = Config
{ cKey :: Key
, cAccount :: Account
}
然后可能有type
或newtypes
使其他参数更具描述性:
-- 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
如果始终需要Config
,那么我建议您使用Reader
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
,它是不被普遍接受的)。
如果您发现自己的类型签名太大了,即使应用了其中一些技术,那么您可能会从错误的方向接近问题,也许这些功能可以分解为更简单的中间步骤,也许这些参数中的一些可以逻辑地组合成更具体的数据类型,也许你只需要一个更大的记录结构来处理设置复杂的操作。它现在已经很开放了。