Haskell:多属性排序不够通用

CMC*_*kai 1 sorting generics haskell

我试图实现一个排序的多属性排序,适用于任何事物的列表.

import Data.Ord (Ordering, Down (..), comparing)
import Data.List (sortBy)
import Data.Monoid (mconcat)

data Order a = ASC a | DESC a

orderBy :: Ord b => [Order (a -> b)] -> [a] -> [a]
orderBy rankedProperties unsorted = 
    sortBy rankedCompare unsorted 
    where 
        rankedCompare x y = 
            mconcat $ map 
                (\property -> 
                    case property of 
                        ASC  f -> comparing f x y
                        DESC f -> comparing (Down . f) x y
                ) rankedProperties
Run Code Online (Sandbox Code Playgroud)

它现在适用于元组和记录,但是我发现了一个问题.问题是bin orderBy必须是相同的.这是考虑到这个:

data Row = Row { shortListed :: Bool, cost :: Float, distance1 :: Int, distance2 :: Int } deriving (Show, Eq)
Run Code Online (Sandbox Code Playgroud)

我希望能够说:orderBy [ASC shortListed, DESC cost] listofrows.

但是回来的错误是:

<interactive>:1:31:
    Couldn't match type ‘Float’ with ‘Bool’
    Expected type: Row -> Bool
      Actual type: Row -> Float
    In the first argument of ‘ASC’, namely ‘cost’
    In the expression: ASC cost
Run Code Online (Sandbox Code Playgroud)

我需要一种方法来使b类型通用,因为b唯一真正必须被comparing函数接受comparing :: Ord a => (b -> a) -> b -> b -> Ordering.

我已经阅读了一些关于存在类型和异类列表的内容,但我不确定如何继续.

And*_*ács 5

由于我们已经Monoid Orderinginstance Monoid b => Monoid (a -> b)in Prelude,我们也Monoid (a -> a -> Ordering)通过迭代函数实例两次.这让我们可以非常简单地解决问题,而不会存在:

import Data.Ord (Ordering, comparing)
import Data.List (sortBy)
import Data.Monoid ((<>), mconcat)

data Row = Row {
  shortListed :: Bool,
  cost :: Float,
  distance1 :: Int,
  distance2 :: Int
  } deriving (Show, Eq)

asc, desc :: Ord b => (a -> b) -> a -> a -> Ordering
asc  = comparing
desc = flip . asc

list :: [Row]
list = [Row False 0 10 20, Row True 10 30 40]

list' :: [Row]
list' = sortBy (asc shortListed <> desc cost <> asc distance1) list
Run Code Online (Sandbox Code Playgroud)

或者:

orderBy :: [a -> a -> Ordering] -> [a] -> [a]
orderBy = sortBy . mconcat

list'' :: [Row]
list'' = orderBy [asc shortListed, desc cost, asc distance1] list
Run Code Online (Sandbox Code Playgroud)