Haskell 与API对话的最佳实践
我正在尝试为Haskell中的API创建一些绑定。我注意到一些函数有大量的参数,例如Haskell 与API对话的最佳实践,haskell,arguments,Haskell,Arguments,我正在尝试为Haskell中的API创建一些绑定。我注意到一些函数有大量的参数,例如 myApiFunction :: Key -> Account -> Int -> String -> Int -> Int -> IO (MyType) 就其本身而言,有这么多争论并不一定是坏事。但作为一个用户,我不喜欢长参数函数。然而,这些参数中的每一个都是绝对100%必要的 有没有一种更像哈斯克尔的方法来抽象这些函数的公共部分?这里的所有内容都是用来构建URL的,所以
myApiFunction :: Key -> Account -> Int -> String -> Int -> Int -> IO (MyType)
就其本身而言,有这么多争论并不一定是坏事。但作为一个用户,我不喜欢长参数函数。然而,这些参数中的每一个都是绝对100%必要的
有没有一种更像哈斯克尔的方法来抽象这些函数的公共部分?这里的所有内容都是用来构建URL的,所以我需要它可用,它代表什么完全取决于函数。但某些事情是一致的,比如键
和帐户
,我想知道对这些参数进行抽象的最佳方法是什么
谢谢大家! 您可以将这些类型组合成更具描述性的数据类型:
data Config = Config
{ cKey :: Key
, cAccount :: Account
}
然后可以使用type
s或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
如果总是需要配置
,那么我建议您使用读卡器
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
根据您的具体应用程序,您还可以将其拆分为多个功能。我以前看过很多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
这些只是少数技术,也有其他技术,但在这之后,它们通常会变得更加复杂。其他人对如何做到这一点会有不同的偏好,我相信很多人都会不同意我的观点,即其中一些做法是否是好的做法(特别是
ConstraintKinds
,它不是普遍接受的)
如果您发现自己的类型签名太大,即使在应用了其中一些技术之后,也可能是从错误的方向处理问题,可能这些函数可以分解为更简单的中间步骤,也许这些参数中的一些可以在逻辑上组合成更具体的数据类型,也许您只需要一个更大的记录结构来处理设置复杂的操作。现在它非常开放。声明一个类型,将所有这些单独的参数捆绑到一个值中?(试图找到逻辑分组通常是有趣的部分。)您可能还想为所有这些
Int
值定义一个newtype
或至少一个type
别名。我在Strave中尝试了许多不同的想法,我的API绑定到Strava。我最终得到了设置类型和镜头。查看一些讨论,了解它现在的样子。我喜欢最后一个。谢谢大家these@Steve它接受ReaderT操作和从ask调用返回的值,然后使用该值运行操作。如果您以前使用过State monad,那么将Reader monad视为一个固定状态,您只能获取它,不能设置它。它通常用于传递静态配置。
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)