Haskell:如何将多个实例放在同一个模块中?

bro*_*s94 3 haskell

假设我有以下代码:

import Data.List.Ordered

data Person = Person String String
     deriving (Show, Eq)

main :: IO ()
main = print . show . sort $ [(Person "Isaac" "Newton"), (Person "Johannes" "Kepler")]
Run Code Online (Sandbox Code Playgroud)

在同一个模块中,我希望能够按名字和姓氏对列表进行排序.显然我不能这样做:

instance Ord Person where
         compare (Person _ xLast) (Person _ yLast) = compare xLast yLast

instance Ord Person where
         compare (Person xFirst _) (Person yFirst _) = compare xFirst yFirst
Run Code Online (Sandbox Code Playgroud)

那么,我的选择是什么?

此页面提到"您可以通过将类型包装在新类型中并将所有必需实例提升到该新类型来实现此目的." 有人能给出一个例子吗?

有没有更好的办法?

huo*_*uon 17

newtype方法将是:

newtype ByFirstname = ByFirstname { unByFirstname :: Person }

instance Ord ByFirstname where
  -- pattern matching on (ByFirstname (Person xFirst _)) etc
  compare [...] = [...]

newtype ByLastname = ByLastname { unByLastname :: Person }

instance Ord ByLastname where
  -- as above
Run Code Online (Sandbox Code Playgroud)

然后排序功能将是这样的:

sortFirstname = map unByFirstname . sort . map ByFirstname
Run Code Online (Sandbox Code Playgroud)

并且类似地ByLastname.


更好的方法是使用sortBy,compareon与对检索姓和名的功能.即

sortFirstname = sortBy (compare `on` firstName)
Run Code Online (Sandbox Code Playgroud)

(在那个注释中,可能值得使用记录类型Person,即data Person = Person { firstName :: String, lastName :: String },一个甚至可以免费获取访问者功能.)


Lui*_*las 9

您不希望Ord仅定义多个实例以按不同的顺序排序.您只需要使用该sortBy函数,该函数将显式比较函数作为其参数.

巧妙的技巧:如果你使用记录类型来定义你的Person类型,导入Data.Function(它给你的on功能)Data.Monoid,你可以使用一些巧妙的技巧,使这更简单,更容易:

import Data.Function (on)
import Data.Monoid (mappend)
import Data.List (sortBy)

data Person = Person { firstName :: String, lastName :: String }

instance Show Person where
    show p = firstName p ++ " " ++ lastName p

exampleData = [ Person "Mary" "Smith"
              , Person "Joe" "Smith"
              , Person "Anuq" "Katig"
              , Person "Estanislao" "Martínez"
              , Person "Barack" "Obama" ]
-- 
-- The "on" function allows you to construct comparison functions out of field
-- accessors:
--
--     compare `on` firstName :: Person -> Person -> Ordering
--     
-- That expression evaluates to something equivalent to this:
--
--    (\x y -> compare (firstName x) (firstName y))
--
sortedByFirstName = sortBy (compare `on` firstName) exampleData
sortedByLastName = sortBy (compare `on` lastName) exampleData

--
-- The "mappend" function allows you to combine comparison functions into a
-- composite one.  In this one, the comparison function sorts first by lastName,
-- then by firstName:
--
sortedByLastNameThenFirstName = sortBy lastNameFirstName exampleData
    where lastNameFirstName = 
              (compare `on` lastName) `mappend` (compare `on` firstName) 
Run Code Online (Sandbox Code Playgroud)