如何概括具有不同 EntityField 值的列表
How to generalize a list with different EntityField values
我尝试概括 URL 处理,例如 /api/v1.0/events?order=-id,title
对于 RESTful 输出 - 因此结果将按 id
desc 排序,并且比通过 title
asc
模型文件:
-- models
Event
title Text
content Text
userId UserId
deriving Eq
deriving Show
Haskell 文件:
-- Events.hs
text2Order :: Text -> [SelectOpt Event]
text2Order text =
case lookup textWithNoPrefix keyVal of
Just val -> [direction val]
Nothing -> error "wrong order"
where
keyVal = [ ("title", EventTitle)
, ("user" , EventUserId)
, ("id" , EventId)
]
textWithNoPrefix = if T.isPrefixOf "-" text
then T.tail text
else text
direction = if T.isPrefixOf "-" text
then Desc
else Asc
我好像有两个问题:
- 编译器不喜欢
keyVal
作为元组列表,其中第二个值不同
- 即使我将
Asc
或 Desc
分配给 direction
,编译器也不接受它
问题是EventTitle
和EventUserId
是不同的类型,所以你不能把他们两个放在同一个列表中。但是,您可以将 EventTitle
和 EventContent
放在同一个列表中——它们的类型都是 EntityField Event Text
.
但是,像下面这样的方法应该可行(使用 Yesod 教程中的 Person 示例):
makeSelectOpt :: (Char,Char) -> SelectOpt Person
makeSelectOpt ('f','+') = Asc PersonFirstName
makeSelectOpt ('f','-') = Desc PersonFirstName
makeSelectOpt ('l','+') = Asc PersonLastName
makeSelectOpt ('l','-') = Desc PersonFirstName
makeSelectOpt ('a','+') = Asc PersonAge
makeSelectOpt ('a','-') = Desc PersonAge
makeSelections :: [(Char,Char)] -> [SelectOpt Person]
makeSelections = map makeSelectOpt
您可以像这样分解出 +/- 处理:
updown '+' = Asc
updown _ = Desc
makeSelectOpt' :: (Char,Char) -> SelectOpt Person
makeSelectOpt' ('f',dir) = updown dir $ PersonFirstName
makeSelectOpt' ('l',dir) = updown dir $ PersonLastName
makeSelectOpt' ('a',dir) = updown dir $ PersonAge
如果要进行错误处理,return一个Maybe (SelectOpt Person)
:
makeSelectOpt'' :: (Char,Char) -> Maybe (SelectOpt Person)
makeSelectOpt'' ('f',dir) = Just $ updown dir $ PersonFirstName
makeSelectOpt'' ('l',dir) = Just $ updown dir $ PersonLastName
makeSelectOpt'' ('a',dir) = Just $ updown dir $ PersonAge
makeSelectOpt'' _ = Nothing
然后:
makeSelectOpts'' :: [(Char,Char)] -> Maybe [SelectOpt Person)
makeSelectOpts'' pairs = mapM makeSelectOpt'' pairs
如果所有对都有效,结果将是 Just [...]
;如果其中任何一对未被识别,结果将是 Nothing
。
更新
这是另一种使用存在类型的方法,它看起来更像您的代码:
{-# LANGUAGE RankNTypes #-}
type ApplyToField = (forall t. EntityField Person t -> SelectOpt Person) -> SelectOpt Person
applyToFirstName, applyToLastName, applyToAge :: ApplyToField
applyToFirstName d = d PersonFirstName
applyToLastName d = d PersonFirstName
applyToAge d = d PersonAge
makeSelectOpt''' :: (Char,Char) -> SelectOpt Person
makeSelectOpt''' (fld,d) = fn (updown d)
where
table = [ ('f',applyToFirstName), ('l',applyToLastName), ('a',applyToAge) ]
fn = case lookup fld table of
Just f -> f
Nothing -> error "bad field spec"
我尝试概括 URL 处理,例如 /api/v1.0/events?order=-id,title
对于 RESTful 输出 - 因此结果将按 id
desc 排序,并且比通过 title
asc
模型文件:
-- models
Event
title Text
content Text
userId UserId
deriving Eq
deriving Show
Haskell 文件:
-- Events.hs
text2Order :: Text -> [SelectOpt Event]
text2Order text =
case lookup textWithNoPrefix keyVal of
Just val -> [direction val]
Nothing -> error "wrong order"
where
keyVal = [ ("title", EventTitle)
, ("user" , EventUserId)
, ("id" , EventId)
]
textWithNoPrefix = if T.isPrefixOf "-" text
then T.tail text
else text
direction = if T.isPrefixOf "-" text
then Desc
else Asc
我好像有两个问题:
- 编译器不喜欢
keyVal
作为元组列表,其中第二个值不同 - 即使我将
Asc
或Desc
分配给direction
,编译器也不接受它
问题是EventTitle
和EventUserId
是不同的类型,所以你不能把他们两个放在同一个列表中。但是,您可以将 EventTitle
和 EventContent
放在同一个列表中——它们的类型都是 EntityField Event Text
.
但是,像下面这样的方法应该可行(使用 Yesod 教程中的 Person 示例):
makeSelectOpt :: (Char,Char) -> SelectOpt Person
makeSelectOpt ('f','+') = Asc PersonFirstName
makeSelectOpt ('f','-') = Desc PersonFirstName
makeSelectOpt ('l','+') = Asc PersonLastName
makeSelectOpt ('l','-') = Desc PersonFirstName
makeSelectOpt ('a','+') = Asc PersonAge
makeSelectOpt ('a','-') = Desc PersonAge
makeSelections :: [(Char,Char)] -> [SelectOpt Person]
makeSelections = map makeSelectOpt
您可以像这样分解出 +/- 处理:
updown '+' = Asc
updown _ = Desc
makeSelectOpt' :: (Char,Char) -> SelectOpt Person
makeSelectOpt' ('f',dir) = updown dir $ PersonFirstName
makeSelectOpt' ('l',dir) = updown dir $ PersonLastName
makeSelectOpt' ('a',dir) = updown dir $ PersonAge
如果要进行错误处理,return一个Maybe (SelectOpt Person)
:
makeSelectOpt'' :: (Char,Char) -> Maybe (SelectOpt Person)
makeSelectOpt'' ('f',dir) = Just $ updown dir $ PersonFirstName
makeSelectOpt'' ('l',dir) = Just $ updown dir $ PersonLastName
makeSelectOpt'' ('a',dir) = Just $ updown dir $ PersonAge
makeSelectOpt'' _ = Nothing
然后:
makeSelectOpts'' :: [(Char,Char)] -> Maybe [SelectOpt Person)
makeSelectOpts'' pairs = mapM makeSelectOpt'' pairs
如果所有对都有效,结果将是 Just [...]
;如果其中任何一对未被识别,结果将是 Nothing
。
更新
这是另一种使用存在类型的方法,它看起来更像您的代码:
{-# LANGUAGE RankNTypes #-}
type ApplyToField = (forall t. EntityField Person t -> SelectOpt Person) -> SelectOpt Person
applyToFirstName, applyToLastName, applyToAge :: ApplyToField
applyToFirstName d = d PersonFirstName
applyToLastName d = d PersonFirstName
applyToAge d = d PersonAge
makeSelectOpt''' :: (Char,Char) -> SelectOpt Person
makeSelectOpt''' (fld,d) = fn (updown d)
where
table = [ ('f',applyToFirstName), ('l',applyToLastName), ('a',applyToAge) ]
fn = case lookup fld table of
Just f -> f
Nothing -> error "bad field spec"