2011-05-09 286 views
80

鑑於爲什麼我不能讓String成爲一個類型類的實例?

data Foo = 
    FooString String 
    … 

class Fooable a where --(is this a good way to name this?) 
    toFoo :: a -> Foo 

我要讓StringFooable一個實例:

instance Fooable String where 
    toFoo = FooString 

GHC則抱怨:

Illegal instance declaration for `Fooable String' 
    (All instance types must be of the form (T t1 ... tn) 
    where T is not a synonym. 
    Use -XTypeSynonymInstances if you want to disable this.) 
In the instance declaration for `Fooable String' 

相反,如果我使用[Char]

instance Fooable [Char] where 
    toFoo = FooString 

GHC抱怨:

Illegal instance declaration for `Fooable [Char]' 
    (All instance types must be of the form (T a1 ... an) 
    where a1 ... an are type *variables*, 
    and each type variable appears at most once in the instance head. 
    Use -XFlexibleInstances if you want to disable this.) 
In the instance declaration for `Fooable [Char]' 

問題

  • 我爲什麼不能做字符串和類型類的實例?
  • 如果我添加一個額外的標誌,GHC似乎願意讓我逃脫。這是一個好主意嗎?
+6

這是我給予好評的那種問題,並標記爲收藏,否則我知道的不久的將來,我會問它;) – 2011-05-09 19:52:04

+3

關於額外的標誌:這可能是一個好主意,只要你信任GHC並理解標誌的作用。想起[Yesod](http://www.yesodweb.com/):它鼓勵您在編寫Yesod應用程序時始終使用OverloadedStrings編譯指示,而QuasiQuotes是Yesod路由規則的必要條件。請注意,在編譯時不要使用標誌,您還可以在.hs文件的頂部放置'{ - #LANGUAGE FlexibleInstances# - }'(或任何其他編譯指示)。 – 2011-05-10 01:39:22

回答

57

這是因爲String僅僅是[Char]一個類型別名,這是對類型Char類型構造[]的只是應用程序,所以這將是形式([] Char)的。這不是(T a1 .. an)的形式,因爲Char不是一個類型變量。

此限制的原因是爲了防止重疊的情況。例如,假設您有instance Fooable [Char],然後有人後來出現並定義了instance Fooable [a]。現在,編譯器將無法確定您想使用哪一個,並且會給您一個錯誤。

通過使用-XFlexibleInstances,您基本上承諾編譯器不會定義任何此類實例。

取決於你想要什麼來完成的,它可能是更好地界定一個包裝:

newtype Wrapper = Wrapper String 
instance Fooable Wrapper where 
    ... 
+4

可以說,爲了爭辯,我確實希望'instance Fooable [a]'。如果'a'是一個Char,有沒有辦法讓'toFoo'函數的行爲不同? – 2011-05-09 23:37:39

+7

@John:有一個擴展名「-XOverlappingInstances」,它將允許這個,並選擇最具體的實例。 [詳見GHC用戶指南](http://www.haskell.org/ghc/docs/latest/html/users_guide/type-class-extensions.html#instance-overlap)。 – hammar 2011-05-09 23:50:03

17

您正在運行到經典Haskell98類型類的兩個限制:

  • 他們禁止在實例類型同義詞
  • 他們不允許嵌套類型不又包含類型變量。

這些嚴格的限制措施是由兩個語言擴展解禁:

  • -XTypeSynonymInstances

,它允許您使用類型synoyms(如String[Char])和:

  • -XFlexibleInstances

它解除了實例類型的限制,其形式爲T a b ..,其中參數爲類型變量。 -XFlexibleInstances標誌允許實例聲明的頭部提及任意的嵌套類型。

請注意,解除這些限制有時會導致overlapping instances,此時可能需要額外的語言擴展來解決歧義問題,從而允許GHC爲您選擇一個實例。


參考文獻:

2

添加到這些問題的答案,如果你不舒服的解除限制,可能有這樣的情況:將字符串換成新類型可能是有意義的,該新類型可以是類的實例。權衡是可能的醜陋,必須在你的代碼中打包和解包。

4

在大多數情況下,FlexibleInstances不是一個好的答案。 更好的替代品包裝的字符串中NEWTYPE或引入一個輔助類,像這樣:

class Element a where 
    listToFoo :: [a] -> Foo 

instance Element Char where 
    listToFoo = FooString 

instance Element a => Fooable [a] where 
    toFoo = listToFoo 

參見:http://www.haskell.org/haskellwiki/List_instance

相關問題