Haskell数据,自定义字符串值

Haskell数据,自定义字符串值,haskell,sdk,http-conduit,Haskell,Sdk,Http Conduit,我正在写一篇文章,我的一切都正常,但是我想在我的搜索过滤器(url参数)中引入更强的类型 示例调用如下所示: -- list first 3 positive comments mentioned by females comments "tide-pods" [("limit", "3"),("sentiment", "positive"),("gender", "female")] config 虽然这对我来说不是太可怕,但我真的希望能够通过以下考试: comments "tide-pod

我正在写一篇文章,我的一切都正常,但是我想在我的搜索过滤器(url参数)中引入更强的类型

示例调用如下所示:

-- list first 3 positive comments mentioned by females
comments "tide-pods" [("limit", "3"),("sentiment", "positive"),("gender", "female")] config
虽然这对我来说不是太可怕,但我真的希望能够通过以下考试:

comments "tide-pods" [("limit", "3"),(Sentiment, Positive),(Gender, Male)] config
或者类似的东西

在DataRank.hs中,您可以看到我的url参数类型
type QueryParameter=(String,String)
,以及转换http管道参数的代码
convertParameters::[QueryParameter]->[(ByteString,可能ByteString)]

我一直在试验数据/类型,例如:

data Gender = Male | Female | Any 
-- desired values of above data types
-- Male = "male"
-- Female = "female"
-- Any = "male,female"
api还需要对任意字符串键、字符串值保持足够的灵活性,因为我希望SDK能够在不依赖SDK更新的情况下提供新过滤器。对于好奇的人来说,最新构建的搜索过滤器列表中有一个到目前为止的搜索过滤器列表


我在Haskell中找到一个提供搜索界面的好方法时遇到了问题。提前谢谢

保持其简单但不安全的最简单方法是只使用带有
任意
字段的基本ADT,该字段接受
字符串
键和值:

data FilterKey
    = Arbitrary String String
    | Sentiment Sentiment
    | Gender Gender
    deriving (Eq, Show)

data Sentiment
    = Positive
    | Negative
    | Neutral
    deriving (Eq, Show, Bounded, Enum)

data Gender
    = Male
    | Female
    | Any
    deriving (Eq, Show, Bounded, Enum)
然后您需要一个函数来将
FilterKey
转换为API的基本
(字符串,字符串)
过滤器类型

filterKeyToPair :: FilterKey -> (String, String)
filterKeyToPair (Arbitrary key val) = (key, val)
filterKeyToPair (Sentiment sentiment) = ("sentiment", showSentiment sentiment)
filterKeyToPair (Gender gender) = ("gender", showGender gender)

showSentiment :: Sentiment -> String
showSentiment s = case s of
    Positive -> "positive"
    Negative -> "negative"
    Neutral  -> "neutral"

showGender :: Gender -> String
showGender g = case g of
    Male   -> "male"
    Female -> "female"
    Any    -> "male,female"
最后,您只需包装基本API的
注释
函数,这样filters参数的类型就更安全了,并在内部转换为
(String,String)
表单来发送请求

comments :: String -> [FilterKey] -> Config -> Result
comments name filters conf = do
    let filterPairs = map filterKeyToPair filters
    commentsRaw name filterPairs conf
这将非常有效,并且非常容易使用:

comments "tide-pods" [Arbitrary "limits" "3", Sentiment Positive, Gender Female] config
但它的可扩展性不强。如果您库的用户希望扩展它以添加
limitint
字段,则必须将其编写为

data Limit = Limit Int

limitToFilterKey :: Limit -> FilterKey
limitToFilterKey (Limit l) = Arbitrary "limit" (show l)
相反,它看起来像

[limitToFilterKey (Limit 3), Sentiment Positive, Gender Female]
这并不特别好,尤其是当他们试图添加许多不同的字段和类型时。一个复杂但可扩展的解决方案是使用单个
过滤器
类型,实际上为了简单起见,让它能够表示单个过滤器或过滤器列表(尝试在
Filter=Filter[(String,String)]
的地方实现它,干净地实现它有点困难):

然后有一个类来表示到
过滤器的转换(很像
Data.Aeson.ToJSON
):

Filter
的例子非常简单

instance FilterKey Filter where
    -- Unsafe because it doesn't match the Fitlers contructor
    -- but I never said this was a fully fleshed out API
    keyToString (Filter (k, _)) = k
    valToString (Filter (_, v)) = v
    toFilter = id
在这里,您可以轻松组合这种类型的值的一个快速技巧是

-- Same fixity as <>
infixr 6 &
(&) :: (FilterKey kv1, FilterKey kv2) => kv1 -> kv2 -> Filter
kv1 & kv2 = toFilter kv1 <> toFilter kv2
加一点糖:

data Is = Is

is :: Is
is = Is

sentiment :: Is -> Sentiment -> Sentiment
sentiment _ = id

gender :: Is -> Gender -> Gender
gender _ = id
您可以编写如下查询

example
    = comments "tide-pods" config
    $ "limit" .= "3"
    & sentiment is Positive
    & gender is Any
如果不将构造函数导出到
Filter
,并且不将
toFilter
导出,则此API仍然是安全的。我把它作为一个方法留在typeclass上,这样
Filter
就可以用
id
覆盖它,从而提高效率。然后,您的库的用户只需执行以下操作

data Limit
    = Limit Int
    deriving (Eq, Show)

instance FilterKey Limit where
    keyToString _ = "limit"
    valToString (Limit l) = show l
如果他们想保持
is
风格,他们可以使用

limit :: Is -> Int -> Limit
limit _ = Limit
写一些类似的东西

example
    = comments "foo" config
    $ limit is 3
    & sentiment is Positive
    & gender is Female

但这只是一个例子,可以让Haskell中的EDSL看起来非常可读。

data FilterKey=touction-mountain-mountain-value | Gender-GenderValue |任意字符串
,然后你可以做
注释“潮汐吊舱”[任意“限制”“3”,情绪积极,性别男性]config
你可以有一个
comments::String->[FilterKey]->config->Result
commentsRaw::String->[(String,String)]->config->Result,其中
comments
调用
commentsRaw
,只需编写一个函数
filterKeyToPair::FilterKey->(String,String)
。那很简单,谢谢!现在,当我迭代要传递给http导管的参数列表时,如何获得所需的值(尤其是ANY=“male,female”?我是否只需执行保护并返回正确的字符串?如果您现在想保持简单,可以执行类似的操作。我通过为每个端点的选项创建数据类型来解决此问题。然后,我使用镜头干净地设置了特定选项。请查看我的库中的示例:。
data Limit
    = Limit Int
    deriving (Eq, Show)

instance FilterKey Limit where
    keyToString _ = "limit"
    valToString (Limit l) = show l
limit :: Is -> Int -> Limit
limit _ = Limit
example
    = comments "foo" config
    $ limit is 3
    & sentiment is Positive
    & gender is Female