2013-02-08 45 views
3

是否有可能有一個類型類型暗示Haskell中的另一個類型類型?例如,讓我們說有一堆「東西」,可以通過「屬性」進行排序:Haskell:Typeclass意味着其他類型類型

data Person = Person { name :: String, age :: Int } 

Person p1 <= Person p1 = (age p1) <= (age p2) 

爲了避免重複一個可以定義一個「訂購的鑰匙」類型的類

class OrdByKey o where 
    orderKey :: (Ord r) => o -> r 
    x <= y = (orderKey x) <= (orderKey y) 

那麼對於Person的實例聲明看起來是這樣的

instance OrdByKey Person where 
    orderKey Person p = age p 

現在這樣做顯然不是出於多種原因的工作。我想知道這是否可能?

+2

如果您的目標僅僅是根據記錄中的特定字段進行排序,您總是可以爲'Person'定義'Ord'實例來比較該特定字段。 – sabauma 2013-02-08 16:52:58

+0

@sabauma是的,但我正在尋找一種「可以通過一些關鍵」進行比較的概括。 – 2013-02-08 16:57:54

+0

對於此特定示例,請查看[comparison](http://hackage.haskell.org/packages/archive/base/latest/doc/html/Data-Ord.html#v:comparing)函數:'比較你的年齡我' – 2013-02-09 00:39:45

回答

2

正如你所指定,在OrdByKey類只能有一個實例 每個類型,當它聽起來像你想能夠聲明實例 在您的記錄類型的每個字段。

要做到這一點,您還必須將字段類型放入類 定義中。這讓你做類似如下:

{-# LANGUAGE MultiParamTypeClasses #-} 

data Person = Person { name :: String, age :: Int } 

class (Ord r) => OrdByKey o r where 
    orderKey :: o -> r 

instance OrdByKey Person Int where 
    orderKey p = age p 

x <=? y = (orderKey x :: Int) <= (orderKey y :: Int) 

但是,你只能有每個字段類型一個實例,因此,如果您 Person類型看起來像

data Person = Person { name :: String, age :: Int, ssn :: String} 

,你將不能夠有要在namessn字段上進行比較的版本。您可以通過將每個字段換成 newtype來解決此問題,因此每個字段都有唯一的類型。所以,你的Person類型看起來像

data Person = Person { name :: Name, age :: Age, ssn :: SSN} 

這將導致大量的newtypes左右浮動,但。

真正的缺點是需要指定 orderKey函數的返回類型。我會建議使用函數從 Data.Function寫出適當的比較函數。我認爲像

compareByKey :: (Ord b) => (a -> b) -> a -> a -> Bool 
compareByKey = on (<=) 

一個 功能概括你的「可以通過一些關鍵比擬」的想法。在這種情況下,您只需要提供 這個函數來提取該密鑰,該密鑰恰好就是您的Person類型的訪問器 函數。

我想不出一個實例,其中OrdByKey類將是有益的,並試圖將<=有多個版本的同一類型的過載好像它會倒在實踐中正確 混淆。

+0

看起來像使用多參數類型的類是乾淨的方式來做到這一點。謝謝! – 2013-02-09 10:52:14

0

你可以這樣做:

instance Ord Person where 
    compare p1 p2 = compare (age p1) (age p2) 

現在的標準<=操作符的作用Person S和比較他們的年齡。

相關問題