2011-05-25 116 views
18

我收到以下錯誤消息,當我編譯:重載函數簽名哈斯克爾

有重複的類型簽名:
weightedMedian.hs:71:0-39:findVal :: [ValPair] - >雙擊 - >雙
weightedMedian.hs:68:0-36:findVal :: [ValPair] - >詮釋 - >雙

我的解決辦法是讓findValI和findValD。但是,findValI只是將Int類型轉換爲Double並調用findValD。

我也可以在各類民(智力,雙人間)不模式匹配,所以我不能只是改變類型簽名

findVal :: [ValPair] -> Num -> Double 

在許多語言中,我不會需要不同的名字。爲什麼在Haskell中需要不同的名稱?這難以添加到語言中嗎?或者那裏有龍嗎?

+1

嘗試刪除函數簽名,將其加載到GHCI中並鍵入':t findVal'來查看自動計算的函數簽名。它可能不是Int或Double,而是看起來像'Num a => [ValPair] - > a - > Double',這正是您需要的。 – 2011-05-25 03:53:00

+0

@Anupam Jain:其實它的「真實」版本是[ValPair] - > Double - > Double。我可以使用Augustss的技術來解決這個問題,然後我最終得到你列出的簽名。 – 2011-05-25 19:12:01

回答

28

特設多態(和名稱超載)是在Haskell類型類通過提供:

class CanFindVal a where 
      findVal :: [ValPair] -> a -> Double 

instance CanFindVal Double where 
    findVal xs d = ... 

instance CanFindVal Int where 
    findVal xs d = findVal xs (fromIntegral d :: Double) 

注意,在這種情況下,由於findVal「真的」需要Double,我只是總是有它取一個double,當我需要傳遞一個int時,只需在調用站點使用fromIntegral。當涉及到實際上不同的行爲或邏輯時,通常需要類型類,而不是混淆。

+2

當然,它的作品,但在這種情況下,不是一個慣用的解決方案。 'CanFindVal'不是一個非常有意義的類型類。 -1 – luqui 2011-05-25 03:39:21

+5

其中sclv種說:「當你實際上涉及不同的行爲或邏輯時,你通常需要類型類,而不是混雜的。」 – MatrixFrog 2011-05-25 03:50:25

+0

哎呀,是的。我的錯。 % - ) – luqui 2011-05-25 04:40:39

6

Haskell不支持C++風格的重載(它與類型類相似,但我們不以相同的方式使用它們)。還有一些與添加它相關的龍,主要是與類型推理有關(變成指數時間或不可判定或類似的東西)。然而,看到這樣的「便利」代碼在Haskell中很少見。哪一個是IntDouble?由於您的Int方法委託給Double方法,我的猜測是Double是「正確的」方法。只用那個。由於文字超載的,你仍然可以稱其爲:

findVal whatever 42 

而且42將被視爲一個Double。唯一的情況是,如果你從根本上得到Int某處,你需要將它作爲這個參數傳遞。然後使用fromIntegral。但是,如果你努力使你的代碼在任何地方都使用「正確」類型,這種情況將不常見(當你必須轉換時,值得引起注意)。

+0

你的問題「哪一個是Int,還是Double?」是非常重要的。我被要求寫一個「加權中值」函數,就像加權平均函數一樣,它需要一堆權重和值,並計算出「加權」中值。如果你的體重只有自然數,那就好了。但是,我們有分數權重,我是要「接近」中位數。這在數學上有點無意義,但在項目的上下文中很有用。所以,是的,它應該是一個Int或Integer,但是我使用雙重來表示權重。這讓我頭痛! – 2011-05-25 18:08:41

13

同時支持findVal :: [ValPair] -> Double -> DoublefindVal :: [ValPair] -> Int -> Double需要ad-hoc多態性(見http://www.haskell.org/haskellwiki/Ad-hoc_polymorphism),這通常是危險的。原因是ad-hoc多態性允許用相同的語法改變語義。

Haskell喜歡所謂的參數多態。您始終可以通過類型簽名來看到這一點,在這裏您有一個類型變量。

Haskell通過類型類支持更安全的ad-hoc多態性版本。

您有三種選擇。

  1. 用明確的函數名繼續你正在做的事情。這是合理的,它甚至被一些c庫使用,例如opengl。
  2. 使用自定義類型類。這可能是最好的方式,但是很沉重並且需要相當數量的代碼(通過haskells非常緊湊的標準)。看看sclv的代碼答案。
  3. 嘗試使用現有的類型類和(如果您使用GHC)獲得專業化的表現。

像這樣:

findVal :: Num a => [ValPair] -> a -> Double 
{-# SPECIALISE findVal :: [ValPair] -> Int -> Double #-} 
{-# SPECIALISE findVal :: [ValPair] -> Double -> Double #-} 
findVal = ... 
+0

我會檢查專業化。這似乎是偷偷摸摸的在後門,但我會接受。 – 2011-05-25 17:43:08

3

在這種情況下,我認爲這是很容易寫,說明如何處理int和double第二個參數的函數。只需編寫findVal,以便在第二個參數上調用realToFrac。這會將Int轉換爲Double,並且僅保留Double。然後讓編譯器爲你推導出類型,如果你懶惰的話。

+0

realToFrac明確地解決了這個問題。我認爲更大的問題是我像一個C++程序員那樣思考..... – 2011-05-25 17:42:05

+0

@Tim Perry有時候如果Haskell有C++類型的重載,那將會很不錯,但通常有很好的解決方法。 – augustss 2011-05-25 19:48:09

+0

@augustss有時候,你的意思是「每當NP完全類型推斷聽起來就像是令人興奮的」? – semicolon 2016-07-15 19:29:14

0

在很多其他編程語言中,您可以聲明(排序)具有相同名稱但其簽名中具有不同其他內容的函數,例如不同的參數類型。這被稱爲重載,當然是實現ad-hoc多態性的最流行的方式。

Haskell故意不支持重載,因爲它的設計者並不認爲它是實現ad-hoc多態性的最佳方式。 Haskell的方式是約束多態,它涉及聲明類型類和類實例