對于 uni 賦值,函式“lookup”已定義為:
lookup :: Env e -> String -> Maybe e
“Env”關鍵字已定義為:
import qualified Data.Map as M
newtype Env e = Env (M.Map String e) deriving (Functor, Foldable, Traversable, Show, Eq, Monoid, Semigroup)
我知道雙冒號“::”的意思是“有型別”。所以查找函式接受兩個引數并回傳一些東西。我知道第二個引數是一個字串,但是,我很難理解第一個引數和輸出。什么是“Env”型別,后面的“e”代表什么。感覺 e 是“Env”的一個引數,但如果是這樣的話,輸出就沒有意義了。
任何幫助,將不勝感激。
uj5u.com熱心網友回復:
感覺 e 是“Env”的一個引數,但如果是這樣的話,輸出就沒有意義了。
這就是語法試圖“感覺像”的方向,因為這就是它的意思。Env是引數化型別,并e作為引數傳遞給該引數。
我不是 100% 確定你為什么認為輸出沒有意義,在這種解釋下?Maybe也是一個引數化型別,并且變數也e被傳遞給; 這與在任何一段代碼中使用兩次變數沒有什么不同。你可以把它想象成這樣的偽代碼:Maybe
lookupType(e) {
argument1_type = Env(e)
argument2_type = String
result_type = Maybe(e)
}
您是否e在型別簽名中考慮的混淆lookup不是被傳遞給Env,而是被定義為接收 的引數Env?這發生在定義Env的地方,而不是在型別中使用的地方。同樣,這就像函式引數在值級別發生的情況一樣;例如,當您撰寫代碼來定義如下函式時:lookupplus
plus x y = x y
然后創建x和y變數以代表plus應用于其他地方的任何內容。但是在您正在使用 plus的另一段代碼中,類似于incr x = plus x 1此處的變數只是作為引數傳遞給plus它,它沒有被定義為的引數plus(實際上它被定義為的引數incr)。
也許您需要更明確地指出這一點。lookup :: Env e -> String -> Maybe e是說:
對于您喜歡的任何型別
e,lookup 接受 anEnv e和 aString并回傳 aMaybe e
因此,您可以傳遞lookupanEnv Integer和 a String,它會給您回傳 a Maybe Integer。或者你可以傳遞一個Env (Maybe [(String, Float)])和一個String,它會給你一個Maybe (Maybe [(String, Float)])。這應該是直觀的,因為它只是在環境中查找鍵;您傳遞給的環境中存盤的任何型別的資料都是可能回傳lookup的型別。lookup
之所以e存在,是因為實際上lookup是引數多型的;這幾乎就像lookup接受一個名為 的型別引數e,然后它可以將其傳遞給其型別簽名中的其他東西。1這就是為什么我以上述方式撰寫偽代碼的原因。
但這正是 Haskell 2中型別簽名中變數的作業方式。您只需在型別簽名中撰寫變數而不定義它們,這意味著您的定義可以在您撰寫變數的位置與任何型別一起使用。變數具有類似名稱e(而不是某種通配符,例如?)的唯一原因是因為您經常需要說您可以使用任何型別,但在幾個不同的地方它必須是相同的型別。lookup就是這樣;它可以采用任何型別Env并回傳任何型別Maybe,但它們必須是一致的。為變數命名e僅允許您鏈接它們以說它們是相同的變數。
1實際上,這正是此型別別在較低級別上的作業方式。Haskell 通常會保持這種型別引數不可見。您只需撰寫一個包含變數的型別簽名,而無需定義它們,并且每次使用隨附的系結時,編譯器都會確定應如何實體化變數。
2在更高級的 Haskell 中,當您打開一堆擴展時,您實際上可以準確控制型別變數的引入位置,而不是總是在您使用變數的每個型別簽名的開頭自動發生。你還不需要知道,我也不打算進一步討論。
uj5u.com熱心網友回復:
我將嘗試給出具體的例子,提供和激發直覺。憑直覺,我認為這個問題有一個非常自然的答案:
e是一個型別變數,并且查找函式希望在所有可能的環境中作業,而不管具體型別e是“。未系結e是語法表達的一種自然方式
第一步,Env型別
該Env型別是容器包中 Data.Map 模塊中Map型別的包裝器。它是鍵值對的集合,您可以插入新的鍵值對并進行查找。如果您正在查找的鍵丟失,您必須回傳錯誤、空值、默認值或其他內容。就像其他編程語言中的哈希圖或字典一樣。
檔案(上面鏈接)寫道
data Map = Map k a從鍵 k 到值 a 的映射。我們會嘗試一下,看看會是什么
k,a可以是什么。我使用 ghci 來獲得互動式反饋。
Prelude> import Data.Map as M
Prelude M> map1 = M.fromList [("Sweden","SEK"),("Chile","CLP")]
Prelude M> :type map1
map1 :: Map [Char] [Char]
Prelude M> map2 = M.fromList [(1::Integer,"Un"),(2,"deux")]
Prelude M> :type map2
map2 :: Map Integer [Char]
Prelude M> map3 = fromList [("Ludvig",10000::Double),("Mikael",25000)]
Prelude M> :type map3
map3 :: Map [Char] Double
可以看到我們根據鍵值對串列創建了各種映射。檔案中的型別簽名Map k a對應不同的k和a上面的ghci會話。For map2,k對應于Integer和ato [Char]。您還可以看到我如何在某些地方使用雙冒號語法宣告手動型別。
為了降低靈活性,我們可以為M.Map. 使用這個包裝器,我們確保鍵始終是字串。
Prelude M> newtype Env e = Env (M.Map String e) deriving (Show)
這個定義說,對于Env e每個. 宣告進一步指出,我們必須始終明確并將映射包裝在值建構式中。讓我們看看我們是否可以做到這一點。eM.Map String enewtypeEnv
Prelude M> Env map1
Env (fromList [("Chile","CLP"),("Sweden","SEK")])
Prelude M> Env map2
<interactive>:34:6: error:
? Couldn't match type ‘Integer’ with ‘[Char]’
Expected type: Map String [Char]
Actual type: Map Integer [Char]
? In the first argument of ‘Env’, namely ‘(map2)’
In the expression: Env (map2)
In an equation for ‘it’: it = Env (map2)
Prelude M> Env map3
Env (fromList [("Ludvig",10000.0),("Mikael",25000.0)])
在上面的會話中,我們看到map1和map3都可以包含在 an 中Env,因為它們具有適當的型別(它們具有k== String),但map2不能(具有k== Integer)。
Map從to的邏輯步驟Env有點棘手,因為還會重命名一些變數。a談論地圖時所呼叫的內容e在 Env 案例中被呼叫。但是變數名總是任意的,所以沒關系。
第二步,查找
我們已經確定這Env e是一種包裝型別,其中包含從字串到某種型別的值的查找表e。你如何查找東西?讓我們從非包裝盒開始,然后是包裝盒。里面有Data.Map一個函式叫做lookup. 讓我們試試吧!
Prelude M> M.lookup "Ludvig" map3
Just 10000.0
Prelude M> M.lookup "Elias" map3
Nothing
好的,要查找一個值,我們提供一個鍵,然后只獲取相應的值。如果鑰匙丟失,我們什么也得不到。回傳值的型別是什么?
Prelude M> :type M.lookup "Ludvig" map3
M.lookup "Ludvig" map3 :: Maybe Double
在 a 中進行查找時Data [Char] Double,我們需要一個型別的鍵[Char]并回傳一個型別的值Maybe Double。好的。這聽起來很合理。其他的例子呢?
Prelude M> :type M.lookup 1 map2
M.lookup 1 map2 :: Maybe [Char]
在 a 中進行查找時Data Integer [Char],我們需要一個型別的鍵Integer并回傳一個型別的值Maybe [Char]。所以一般來說,要查找我們需要一個型別的鍵k和一個型別的映射M.Map k a并回傳一個Maybe a.
一般來說,我們認為M.lookup :: k -> M.Map k a -> Maybe a。讓我們看看 ghci 是否同意。
Prelude M> :t M.lookup
M.lookup :: Ord k => k -> Map k a -> Maybe a
確實如此!它還要求k型別必須是某種具有定義順序的型別,例如字串或數字。這就是Ord k =>事情一開始的意義。為什么?好吧,它與M.Map型別的定義方式有關......現在不要太在意它。
如果我們設定k為 ,我們可以專門化型別簽名String。在那種特殊情況下,它看起來像
M.lookup :: String -> Map String a -> Maybe a
唔。這啟發我們翻轉引數 1 和 2 的順序進行查找,并將變數替換a為e. 它只是一個變數,無論如何變數上的名稱都是任意的......讓我們呼叫這個新函式myLookup
myLookup :: Map String e -> String -> Maybe e
并且由于Env a本質上與 相同Map String e,如果我們只是打開 Env 型別,我們可以定義
myLookup :: Env a -> String -> Maybe e
如何實作這樣的功能?一種方法是通過模式匹配來解開型別,然后讓庫承擔繁重的作業。
myLookup (Env map) key = M.lookup key map
結論
我嘗試使用 Haskell 標準庫中的函式和型別來構建具體示例。我希望我已經說明了多型型別的概念(即M.Map k a帶有 ak和 a的型別v)并且它Env是一個包裝器,專門用于kString 的情況。
我希望這個具體的例子能說明為什么型別簽名看起來像它們,以及為什么型別變數是有用的。
我沒有試圖用術語給你正式的待遇。我沒有解釋 Maybe 型別,也沒有解釋型別類和派生型別類。我希望你可以在其他地方閱讀。
我希望它有所幫助。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/528536.html
標籤:哈斯克尔
上一篇:將受約束的文字分配給多型變數
