How would I make a generic sort option SelectOpt for all columns?
-
21-12-2019 - |
Question
Is there a more elegant way of writing this: (the code below compiles)
getModelSorterOpt sortOrder sortField = do
case sortOrder of
"Asc" ->
case sortField of
"id" -> Just $ Asc ModelId
"name" -> Just $ Asc ModelName
"created" -> Just $ Asc ModelCreated
_ -> Nothing
"Desc" ->
case sortField of
"id" -> Just $ Desc ModelId
"name" -> Just $ Desc ModelName
"created" -> Just $ Desc ModelCreated
_ -> Nothing
The main problem is that I can't use a variable instead of the "Asc/Desc" case. Something like the code below, fails to typecheck.
getModelSorterOpt sortOrder sortField =
case sortField of
"id" -> Just $ sortOrder' ModelId
"name" -> Just $ sortOrder' ModelName
"created" -> Just $ sortOrder' ModelCreated
_ -> Nothing
where sortOrder' = case sortOrder of
"Asc" -> Asc
"Desc" -> Desc
GHC says that it doesn't expect ModelId and ModelName to be a part of the same case since they're different types. Which I think is valid. However, Asc ModelId
is the same type as Asc ModelName
. How do I tell GHC that?
Hammar's initial answer works. This version still doesn't work:
getModelSorterOpt sortOrder sortField = do
(sortOrder'' :: EntityField Model a -> SelectOpt Model ) <- sortOrder'
case sortField of
"id" -> Just $ sortOrder'' ModelId
"name" -> Just $ sortOrder'' ModelName
"created" -> Just $ sortOrder'' ModelCreated
_ -> Nothing
where sortOrder' = case sortOrder of
"Asc" -> Just Asc
"Desc" -> Just Desc
_ -> Nothing
I get the same error that ModelId
and ModelName
are of different types. Is this because sortOrder''
is being used monadically?
Solution
Try giving sortOrder'
a type signature.
where sortOrder' :: EntityField Model typ -> SelectOpt Model
sortOrder' = case sortOrder of
"Asc" -> Asc
"Desc" -> Desc
This compiles using GHC 7.6.2.
OTHER TIPS
I've modified your design a bit by putting the lookup keys in an association list. Moving data into data structures rather than control structures seems like a win. I'm also making some assumptions about your types (or types similar to them). Here are two options depending on whether or not you like/grok Applicatives:
module Sorting where
import Control.Applicative ((<*>))
data Field = ModelId | ModelName | ModelCreated
data Sorting = Asc Field | Desc Field
orderMap :: [(String, Field -> Sorting)]
orderMap =
[ ("Asc", Asc)
, ("Desc", Desc)
]
fieldMap :: [(String, Field)]
fieldMap =
[ ("id", ModelId)
, ("name", ModelName)
, ("created", ModelCreated)
]
lookupSorting :: String -> String -> Maybe Sorting
lookupSorting orderName fieldName = do
order <- lookup orderName orderMap
field <- lookup fieldName fieldMap
return (order field)
lookupSorting' :: String -> String -> Maybe Sorting
lookupSorting' orderName fieldName =
lookup orderName orderMap <*> lookup fieldName fieldMap