2012-03-27 104 views
2

我有一個函數,它有類型Read a => String -> a,是否有可能有另一個功能具有相同的名稱,做不同的事情a是例如String?是否有任何GHC擴展允許這樣做?Haskell中的函數類型專業化

喜歡的東西:

f :: Read a => String -> a 
f = read 

f :: String -> String 
f = id 
+0

如果您只關心性能,那麼編譯器應該優化這些類型的東西。 – leftaroundabout 2012-03-27 00:31:28

+0

我不關心性能,我想有一個版本,不需要周圍的字符串引號read'的',但工作正常'read'的一切。 – 2012-03-27 00:37:46

+0

問題http://stackoverflow.com/questions/9870962/haskell-making-a-superclass-of-num/地址完全相同的問題;有些答案可能有用。 – 2012-03-27 01:23:32

回答

12

在Haskell,這種函數重載(廣告-hoc多態性)是通過使用類型類實現的,而不是通過在多個類型下綁定相同的名稱。

{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-} 

class F a where f :: String -> a 

instance F String where f = id 
instance F Int where f = read 
instance F Char where f = read 
instance F Float where f = read 
-- etc. 

現在,f可以對其中的F實例已被宣佈任何類型的工作。

不幸的是,你不能逃脫如下:

instance Read a => F a where f = read 

也許unintuitively,這並不僅適用於有Read一個實例類型聲明的F一個實例。因爲GHC解決只用實例聲明(部分爲=>的右側)的頭部的情況下,這實際上宣告各類a是的F情況下,反而使得它一個類型錯誤調用f任何東西這不也是Read的實例。

如果您啓用UndecidableInstances擴展名,它會編譯,但這隻會導致其他問題。這是一個你不想冒險的兔子洞。

相反,您應該針對您打算運行的每個單獨類型聲明F的實例。這不是一個簡單的類像這樣的一個非常繁重的,但如果你使用最新GHC的版本,可以使用以下使它稍微容易:現在

{-# LANGUAGE DefaultSignatures #-} 

class F a where f :: String -> a 
       default f :: Read a => String -> a 
       f = read 

,對於任何類型的是的Read例如,你可以聲明其F實例,而無需提供f實施明確:

instance F Int 
instance F Char 
instance F Float 
-- etc. 

對於任何類型的Read情況下,你仍然必須寫明確的實施。

4

我得到它的工作,但我不得不打開了一堆可疑的語言選項:

{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE UndecidableInstances #-} 
{-# LANGUAGE TypeSynonymInstances #-} 
{-# LANGUAGE OverlappingInstances #-} 

class SpecialRead a where 
    specialRead :: String -> a 

instance Read a => SpecialRead a where 
    specialRead = read 

instance SpecialRead String where 
    specialRead = id 

main = do 
    print (specialRead "10" :: Int) 
    print (specialRead "10" :: String) 
+5

'FlexibleInstances'和'TypeSynonymInstances'是無爭議的;他們只是放鬆了Haskell標準強加的一些限制,但沒有什麼可擔心的。然而,'UndecidableInstances'和'OverlappingInstances'是危險區域,除非你知道你在做什麼,否則通常應該避免。 – bitbucket 2012-03-27 01:12:15