說,比如我有一個類型的類,例如,對於每一個實例的這種類型的類我有一個函數如何指定從中調用函數
f :: [Bool] -> Maybe (a, [a])
我怎樣才能調用F?更確切地說,如果b是另一個在此類型中的類型,我怎麼能指定我是從a的實例開始的而不是從b的實例開始的?
說,比如我有一個類型的類,例如,對於每一個實例的這種類型的類我有一個函數如何指定從中調用函數
f :: [Bool] -> Maybe (a, [a])
我怎樣才能調用F?更確切地說,如果b是另一個在此類型中的類型,我怎麼能指定我是從a的實例開始的而不是從b的實例開始的?
有時您必須添加類型註釋。考慮類型類Read
:
class Read a where
read :: String -> a
如果您嘗試read "1"
期望得到的整數1
,你會得到一個錯誤,而不是因爲沒有辦法知道什麼類型a
是。但是,這工作:read "1" :: Int
。
如果你正在使用你的函數f
在編譯器可以找出什麼a
的情況下,那麼它會正常工作。否則,您必須通過添加適當的類型註釋來幫助編譯器。
typeclass實例通過聲明它們的特定類型進行索引。例如,在以下情況下:
class YourClass a where
f :: [Bool] -> Maybe (a, [a])
instance YourClass Int where
f = error "Define me"
instance YourClass Char where
f = error "Define me"
有YourClass
兩個獨特的情況。當a
是Int
時,另一個 - 當它是Char
。這些實例使用f
函數實際應用的類型自動解析。
因此,只要您使用f
就好像它具有特定簽名[Bool] -> Maybe (Int, [Int])
一樣,Int
的實例會自動拾取。由於Haskell也有非常強大的類型推斷,所以在大多數情況下並不需要指定這個特定的簽名,編譯器會自動從上下文中解析它。
所以基本上,你可以簡單地在你有實例的所有類型上使用該函數,並且安全地期望編譯器爲你完成剩下的工作。
爲了補充其他答案,我會指出確實有些情況可能是不明確的。
例如:
class C a where
f :: String -> a
instance C Int where
f s = length s
instance C Bool where
f s = s == "hello!"
main :: IO()
main = print (f "testing")
現在,print
接受任何(Show
能)類型,並且f "testing"
能產生兩者Int
和Bool
。由於上面的程序可能會打印出「False」以及「7」,所以這本質上是不明確的。編譯器無法解決這個問題。
作爲一個解決方案,我們可以使用
main = print (f "testing" :: Int)
-- or
main = print (f "testing" :: Bool)
消除歧義。另一個不太方便的選項是
main = print ((f :: String -> Int) "testing")
甚至在GHC 8。0,右邊的分機上,
main = print (f @ Int "testing") -- explicit choice for type a
但請注意,在某些情況下,沒有歧義,GHC可以創造奇蹟。例如:
main = print (f "testing" && True) -- Bool is chosen here
main = print (f "testing" + length [1,2]) -- Int is chosen here
這是因爲&&
需要Bool
和length
回報Int
而+
迫使兩個參數具有相同類型的(所以f "testing"
必須Int
以及)。
也正在
bar :: Bool -> Float
bar b = ...
main = print (bar (f "testing)) -- Bool is chosen
嗯,'Num','Read','Show' ... – dfeuer
好了,但還有的arent情況時,這是曖昧?例如,在我的具體情況下,我想在這樣的case表達式中使用f:case f xs Nothing - > something ...只是(a,as) - >別的東西... Haskell如何知道我是哪個使用? – user3726947
@ user3726947如果你有這個表達式的函數不關心哪個特定類型爲「a」,那麼它是一個多態函數,並且將具有像'f2 ::(YourClass a)=> a - > ...' –