使用型別化的孔來定義類例項
通過互動式過程,鍵入的孔可以更容易地定義功能。
假設你要定義一個類例項 Foo Bar
(對於你的自定義 Bar
型別,以便將其與一些需要 Foo
例項的多型庫函式一起使用)。你現在傳統上會查詢 Foo
的文件,找出你需要定義哪些方法,仔細檢查它們的型別等等 - 但是對於打字的洞,你實際上可以跳過它!
首先只定義一個虛擬例項:
instance Foo Bar where
編譯器現在會抱怨
Bar.hs:13:10: Warning:
No explicit implementation for
‘foom’ and ‘quun’
In the instance declaration for ‘Foo Bar’
好的,所以我們需要為 Bar
定義 foom
。但是,什麼是,即使應該是什麼?我們再一次懶得檢視文件,只要問編譯器:
instance Foo Bar where
foom = _
在這裡,我們使用了一個打字的洞作為一個簡單的文件查詢。編譯器輸出
Bar.hs:14:10:
Found hole ‘_’ with type: Bar -> Gronk Bar
Relevant bindings include
foom::Bar -> Gronk Bar (bound at Foo.hs:4:28)
In the expression: _
In an equation for ‘foom’: foom = _
In the instance declaration for ‘Foo Bar’
請注意編譯器如何使用我們想要例項化的具體型別 Bar
填充類型別變數。這可以使簽名比類文件中的多型變數更容易理解,特別是如果你正在處理更復雜的方法,例如多引數型別類。
但到底是怎麼回事?在這一點上,問問 Hayoo 可能是個好主意。但是我們仍然可以在沒有這個的情況下轉義:作為盲目的猜測,我們假設這不僅是一個型別建構函式,而且是單值建構函式,即它可以用作以某種方式產生 Gronk a
值的函式。所以我們試試
instance Foo Bar where
foom bar = _ Gronk
如果我們很幸運,Gronk
實際上是一個值,編譯器現在會說
Found hole ‘_’
with type: (Int -> [(Int, b0)] -> Gronk b0) -> Gronk Bar
Where: ‘b0’ is an ambiguous type variable
好吧,這很難看 - 首先請注意 Gronk
有兩個引數,所以我們可以改進我們的嘗試:
instance Foo Bar where
foom bar = Gronk _ _
現在這很清楚了:
Found hole ‘_’ with type: [(Int, Bar)]
Relevant bindings include
bar::Bar (bound at Bar.hs:14:29)
foom::Bar -> Gronk Bar (bound at Foo.hs:15:24)
In the second argument of ‘Gronk’, namely ‘_’
In the expression: Gronk _ _
In an equation for ‘foom’: foom bar = Gronk _ _
你現在可以通過例如解構 bar
值來進一步推進(然後元件將在 Relevant bindings
部分中顯示型別)。通常情況下,在某種程度上,正確定義將是完全明顯的,因為你看到所有可用的論點和型別都像拼圖一樣融合在一起。或者,你可能會看到定義不可能以及原因。
所有這些在具有互動式編譯的編輯器中效果最佳,例如具有 haskell 模式的 Emacs。然後,你可以使用型別化的洞,就像 IDE 中的滑鼠懸停值查詢一樣,用於解釋的動態命令式語言,但沒有所有限制。