我正在为一项服务构建一个API客户端,并且在原始API上使用该API的主要原因是为用户呈现所有不错的类型。
我为可选查询参数创建了一种类型,该查询参数对于每个端点都是不同的(而不是强制用户提供字符串元组列表):
data ListDatasetsParams = ListDatasetsParams {
offset :: Maybe Int,
limit :: Maybe Int,
desc :: Maybe Bool,
unnamed :: Maybe Bool
}
现在我想将此类型转换为具有签名的查询
type Query = [QueryItem]
type QueryItem = (ByteString, Maybe ByteString)
我真的没有找到一种方法,至少有点普通。使它正常工作的唯一方法是使用纯硬代码,在其中检查每个参数并手动添加正确的字符串。
我可以做得更好吗?还是在Haskell中还有其他惯用的方式做到这一点?
答案 0 :(得分:3)
您可以使用generics进行此操作:
{-# LANGUAGE DefaultSignatures, DeriveGeneric, FlexibleContexts, FlexibleInstances, TypeOperators #-}
import GHC.Generics
import Data.ByteString (ByteString)
import Data.String
type Query = [QueryItem]
type QueryItem = (ByteString, Maybe ByteString)
class ToByteString a where
toByteString :: a -> ByteString
default toByteString :: Show a => a -> ByteString
toByteString = fromString . show
instance ToByteString Int
instance ToByteString Bool
instance ToByteString [Char] where
toByteString = fromString
class GToQuery f where
gToQuery :: f a -> Query
instance (GToQuery a, GToQuery b) => GToQuery (a :*: b) where
gToQuery (a :*: b) = gToQuery a ++ gToQuery b
instance GToQuery a => GToQuery (M1 D c a) where
gToQuery (M1 x) = gToQuery x
instance GToQuery a => GToQuery (M1 C c a) where
gToQuery (M1 x) = gToQuery x
instance (Selector c, ToByteString a) => GToQuery (M1 S c (K1 i (Maybe a))) where
gToQuery s@(M1 (K1 x)) = [(toByteString (selName s), fmap toByteString x)]
class ToQuery a where
toQuery :: a -> Query
default toQuery :: (Generic a, GToQuery (Rep a)) => a -> Query
toQuery = gToQuery . from
data ListDatasetsParams = ListDatasetsParams {
offset :: Maybe Int,
limit :: Maybe Int,
desc :: Maybe Bool,
unnamed :: Maybe Bool
} deriving(Generic)
instance ToQuery ListDatasetsParams
它是这样的:
*Main> toQuery (ListDatasetsParams (Just 1) Nothing (Just True) Nothing )
[("offset",Just "1"),("limit",Nothing),("desc",Just "True"),("unnamed",Nothing)]
*Main>