将数据类型转换为查询字符串

时间:2019-09-07 01:31:17

标签: haskell

我正在为一项服务构建一个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中还有其他惯用的方式做到这一点?

1 个答案:

答案 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>