Haskell 如何用不同的EntityField值概括列表

Haskell 如何用不同的EntityField值概括列表,haskell,yesod,persistent,Haskell,Yesod,Persistent,例如,对于RESTful输出,我尝试将URL处理概括为/api/v1.0/events?order=-id,title,因此结果将按iddesc排序,而不是按titleasc排序 模型文件: -- models Event title Text content Text userId UserId deriving Eq deriving Show Haskell文件: -- Events.hs text2Order :: Text -> [S

例如,对于RESTful输出,我尝试将URL处理概括为
/api/v1.0/events?order=-id,title
,因此结果将按
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
    分配给
    方向
    编译器也不接受它

  • 问题是
    EventTitle
    EventUserId
    属于不同的类型,因此不能将它们放在同一个列表中。但是,您可以将
    EventTitle
    EventContent
    放在同一个列表中——它们都有类型
    EntityField事件文本

    但是,类似以下的方法应该可行(使用Yesod教程中的Person示例):

    您可以这样计算+/-处理:

    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
    
    如果要执行错误处理,请返回
    Maybe(选择opt Person)

    然后:

    makeSelectOpts'' :: [(Char,Char)] -> Maybe [SelectOpt Person)
    makeSelectOpts'' pairs = mapM makeSelectOpt'' pairs
    
    如果所有对都有效,则结果将是
    仅[…]
    ,如果其中任何一对未被识别,则结果将是

    更新

    下面是另一种使用存在类型的方法,它看起来更像您的代码:

    {-# 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"
    

    谢谢分解方向有点困难,因为我们可以有
    id
    -id
    (即no
    +id
    )。不是真的-只返回一个Maybe(SelectOpt Person)。我会更新我的答案。好吧-我不会更新我的答案-我已经写够了。如果你觉得其中有什么有用的话,也许你可以投赞成票?该过程的要点是,将web参数规范化为规范形式,例如,一个字符对列表。然后,您可以使用我展示的内容将这些对转换为SelectOpt Person列表。如果某件事情没有意义或者需要忽略它,请在规范化步骤中执行此操作。规范化步骤只处理字符串,因此不会遇到任何棘手的类型问题。
    makeSelectOpts'' :: [(Char,Char)] -> Maybe [SelectOpt Person)
    makeSelectOpts'' pairs = mapM makeSelectOpt'' pairs
    
    {-# 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"