Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/mercurial/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 与API对话的最佳实践_Haskell_Arguments - Fatal编程技术网

Haskell 与API对话的最佳实践

Haskell 与API对话的最佳实践,haskell,arguments,Haskell,Arguments,我正在尝试为Haskell中的API创建一些绑定。我注意到一些函数有大量的参数,例如 myApiFunction :: Key -> Account -> Int -> String -> Int -> Int -> IO (MyType) 就其本身而言,有这么多争论并不一定是坏事。但作为一个用户,我不喜欢长参数函数。然而,这些参数中的每一个都是绝对100%必要的 有没有一种更像哈斯克尔的方法来抽象这些函数的公共部分?这里的所有内容都是用来构建URL的,所以

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

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)