2015-12-22 24 views
1

我想使用創建的操作員「幻影鍵入」單位:在功能定義中使用的實例圖案

newtype Length (a::UnitLength) b = Length b deriving (Eq) 
data UnitLength = Meter 
      | KiloMeter 
      | Miles 
      deriving (Eq,Show) 

class OperAdd a b c where 
    (<+>) :: a -> b -> c 

instance Num val => OperAdd (Length a val) (Length b val) (Length c val) where 
    (<+>) (Length la) (Length lb) = if a == b 
             then Length (la+lb) 
             else ... 

爲了避免重複實例聲明爲每個單位,我想用的類型abc在實例聲明中進行自動單位轉換。

是否可以在我的操作符定義中使用abc

+2

難道是你正在尋找的功能是「TypeFamilies」 - 我會以此爲藉口來鏈接我最喜歡的haskell blogpost https://www.fpcomplete.com/school/to-infinity-and-超越/接機的星期幾/型家庭,和口袋妖怪 – epsilonhalbe

回答

2

不,因爲Haskell將類型級變量和值級變量嚴格分開。您確定要在類型級別上處理物理單元嗎?物理尺寸,這確實有意義(請查看,例如units包),但爲什麼你需要不同類型的不同單位長度,看到他們顯然是相等的呢?好吧,這可能是有意義的,以避免昂貴的隱式單位轉換,但是爲什麼你定義了一個運算符,它可以做更多或者更少的隱式單位轉換呢?

無論如何,您不能使結果類型c依賴於(值級)if語句。基本上你需要寫出一個類型級別case /模式匹配;最簡單的方式做到這一點是有多個實例:

instance Num n => OperAdd (Length Metre n) (Length Metre n) (Length Metre val) where 
    (<+>) (Length la) (Length lb) = Length (la+lb) 
instance Num n => OperAdd (Length Metre n) (Length KiloMetre n) (Length Metre n) where 
    (<+>) (Length la) (Length lb') = Length (la+lb*1000) 
instance Num n => OperAdd (Length Metre n) (Length Metre n) (Length KiloMetre n) where 
    (<+>) (Length la) (Length lb) = Length $ (la+lb)/1000 
... 

一個更通用的解決方案,看起來更類似於您最初的想法可能是可能的singletons