2017-02-11 59 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,它实现了一个类型安全的异构映射,更像一个关于这个想法如何轻浮而不是一个有用模块的艺术作品。但是,嘿,让我知道如果你发现它的用处。 ;-)

也许你应该退后一步,描述你正试图解决的更大问题,以至于你觉得你需要这个。我向你保证有一个更好的方法。

相关问题