2017-02-11 56 views
1

所以,如果我想創建例如與生存型HashMap的鍵像我可以使用unsafecorce作爲存儲類型類的hashkey/comaprison鍵嗎?

data SomeId = forall a. SomeClass a => SomeId a 

所以,如果我想創建一個地圖我需要實現我自己Ord。有沒有辦法只存儲的價值?在這種情況下,是unsafeCooerce永久或有任何警告?

是否這樣?

instance Ord SomeId where 
    compare (Id a) (Id b) = compare (unsafeCoerce a)::Int (unsafeCoerce b)::Int 

有沒有更好的方法來做到這一點?

+0

'HashMap'鍵不需要'Ord' - 只有'Map'。你究竟在做什麼? – Alec

回答

5

我不清楚你在找什麼(也可能不是你)。這裏是你如何爲你的存在數據類型實現EqHashable

您將需要一個HashableTypeable,並Eq約束添加到存在(除了你首先想有什麼其他的限制 - 我會用Show)。

import Data.Typeable 
import Data.Hashable 

data SomeId = forall a. (Show a, Hashable a, Eq a, Typeable a) => SomeId a 

-- This instance uses the fact 'SomeId' wraps types that have and 'Eq' and 
-- a 'Typeable' constraint 
instance Eq SomeId where 
    SomeId a == SomeId b = maybe False (a ==) (cast b) 

-- This instance uses the fact 'SomeId' wraps types that have 'Hashable' constraints 
instance Hashable SomeId where 
    hashWithSalt s (SomeId a) = 0xdeadbeef * hashWithSalt s a 

而且,對於調試目的:現在

instance Show SomeId where 
    show (SomeId x) = show x 

,我可以使用SomeId如在HashMap的關鍵。例如,

ghci> import qualified Data.HashMap.Strict as H 
ghci> hashMap = H.fromList [(SomeId 1, "one"), (SomeId(), "unit"), (SomeId "s", "string")] 
ghci> H.lookup (SomeId 1) hashMap 
Just "one" 
ghci> H.lookup (SomeId()) hashMap 
Just "unit" 
ghci> H.lookup (SomeId "s") hashMap 
Just "string 
ghci> H.lookup (SomeId 2) hashMap 
Nothing 
ghci> H.lookup (SomeId True) hashMap 
Nothing 

作爲最後的一句話:注意,你是把上SomeIdTypeableEq都這麼導出滿足這些界限是幾乎沒有困難,因爲它最初看起來可能最初限制。


如果不明確,我向您展示一個替代您的unsafeCoerce。這種方法是不可取的。特別是,它

  • 違反一般
  • 引用透明完全忽視所有的法律爲EqOrd

總之 - 這是行不通的,即使它沒有它會創造一系列難以複製和混淆的錯誤。

+0

爲什麼我沒有考慮轉發到類型自己的'Eq'我不知道......有時候我們太依附於某些硬件 – luqui

+1

@luqui也在那裏。 :)在另一個註釋中,「異地圖」非常整潔。很好的使用等級2的類型。 – Alec

4

這是可怕的,你絕對不應該這樣做。 unsafeCoerce這裏可能會返回不同的值,取決於表達式是否已經被評估過,如果GC決定移動對象(並且GHC有一個壓縮收集器AFAIK,這樣就會一直髮生),甚至可能依賴於沒有任何東西的對象通過閱讀結束來處理它。可怕的,可怕的想法,這。

參照平等的測試,就像你在最命令式語言看

var foo = Object(); 
var bar = Object(); 
foo === foo // true 
foo === bar // false 

是不可能在Haskell,因爲引用透明,它說,你可以隨時替換其定義,什麼都不會變更改。如果它不被命名的語言有引用透明,你可以用替換代碼:

Object() === Object() // true 
Object() === Object() // false 

這顯然是一個矛盾。引用透明是使Haskell如此高興的事情之一,但不幸的是,如果我推斷你正在嘗試做什麼,它將會使它變得不可能。我建議不要繞開參考透明度(你可以使用unsafePerformIO) - Haskell 極其如果沒有它,很難推理 - 整個語言都是圍繞這個假設建立的。

所以,有了這個警告,這裏有一些筆記,無論如何funzies,因爲我已經下了這個特殊的兔子洞。

它看起來像我在模擬從Id到它們所指的對象的地圖,而且這個地圖可以是多態的。即也許你想喜歡的功能:與???一些合適的值,因爲我們已經忘記了類型信息這是不可能的

insert :: SomeClass a => a -> MyMap -> (SomeId, MyMap) 
lookup :: MyMap -> SomeId -> ??? 

。你可以使用Data.Dynamic,我想:

lookup :: MyMap -> SomeId -> Maybe Dynamic 

根據你想要做什麼。這將涉及很多鑄造,你基本上是通過這樣做把靜態類型安全性扔出窗外。

也許你想考慮鍵入標識符?

insert :: SomeClass a => a -> MyMap -> (SomeId a, MyMap) 
lookup :: MyMap -> SomeId a -> Maybe a 

如果你有SomeClass需要一些獨特的「哈希」生成功能(它是真正保證是甚至跨類型獨特的),那麼你可以使這個API的工作。如果發生衝突,那麼出現錯誤unsafeCoerce錯誤(例如,在ghci中嘗試unsafeCoerce 42 "foo" :: String)。您可以通過在您的Id類型中添加TypeRep來緩解一些痛苦,但您仍然必須自行決定如何對這些值進行哈希處理。

你可以用新生成的Unique時,你的密鑰對的值,使用unsafePerformIO,但你會違反參照透明度,如果你這樣做,因爲如果你生成具有相同值的兩個鍵,就會比較不同,這在純語言中是無稽之談。

基本上,一個異構地圖的整個想法是困難的。

我寫了hetero-map,它實現了一個類型安全的異構映射,更像一個關於這個想法如何輕浮而不是一個有用模塊的藝術作品。但是,嘿,讓我知道如果你發現它的用處。 ;-)

也許你應該退後一步,描述你正試圖解決的更大問題,以至於你覺得你需要這個。我向你保證有一個更好的方法。

相關問題