Haskell 我可以使用unsecfect作为存在类型类的hashkey/comaprison键吗?

Haskell 我可以使用unsecfect作为存在类型类的hashkey/comaprison键吗?,haskell,dictionary,Haskell,Dictionary,所以,如果我想创建一个hashmap键,例如,使用 data SomeId = forall a. SomeClass a => SomeId a 所以,如果我想创建一个映射,我需要自己实现Ord。有没有一种方法可以存储值?在这种情况下,Int是永久的还是有任何警告 像这样 instance Ord SomeId where compare (Id a) (Id b) = compare (unsafeCoerce a)::Int (unsafeCoerce b)::Int 有

所以,如果我想创建一个hashmap键,例如,使用

data SomeId = forall a. SomeClass a => SomeId a
所以,如果我想创建一个映射,我需要自己实现Ord。有没有一种方法可以存储值?在这种情况下,Int是永久的还是有任何警告

像这样

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

有更好的方法吗?

我不清楚您在寻找什么,您也可能不清楚。下面是如何为存在数据类型实现Eq和Hashable

除了您首先需要的任何其他约束之外,您还需要向存在约束添加一个Hashable、Typeable和Eq约束——我将使用Show

并且,出于调试目的:

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
最后一句话:请注意,在您对SomeId施加的初始约束中,Typeable和Eq都是可派生的,因此满足这些边界并不像最初看起来那么困难

如果不清楚的话,我会给你一个替代你的不完美的选择。这种方法是。。。不可取。特别是

违反了一般的引用透明度 完全无视Eq和Ord的所有法律
简而言之,它不会起作用,即使它起作用了,也会产生大量难以复制和令人困惑的错误。

我不清楚您在寻找什么,您也可能不清楚。下面是如何为存在数据类型实现Eq和Hashable

除了您首先需要的任何其他约束之外,您还需要向存在约束添加一个Hashable、Typeable和Eq约束——我将使用Show

并且,出于调试目的:

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
最后一句话:请注意,在您对SomeId施加的初始约束中,Typeable和Eq都是可派生的,因此满足这些边界并不像最初看起来那么困难

如果不清楚的话,我会给你一个替代你的不完美的选择。这种方法是。。。不可取。特别是

违反了一般的引用透明度 完全无视Eq和Ord的所有法律
简言之,它不会工作,即使它工作了,也会产生大量难以复制和令人困惑的错误。

这太糟糕了,你绝对不应该这样做。如果GC决定移动对象,而GHC有一个压缩收集器AFAIK,则此处的unsafeCoerce可能会返回不同的值,这取决于表达式是否已被求值,因此这种情况将一直发生,甚至可能取决于通过读取到末尾与之无关的对象。糟糕,糟糕的主意,这个

引用相等测试,就像您在大多数命令式语言中看到的那样

var foo = Object();
var bar = Object();
foo === foo   // true
foo === bar   // false
在Haskell中是不可能的,因为引用的透明性,这意味着您总是可以用变量的定义替换变量,并且什么都不会改变。如果不应命名的语言具有引用透明性,则可以将该代码替换为:

 Object() === Object()  // true
 Object() === Object()  // false
这显然是一个矛盾。引用透明性是让Haskell如此好用的因素之一,但不幸的是,如果我推断你想做什么,这将使它变得不可能。我建议不要绕过引用透明性,使用unsafePerformIO可以做到这一点——没有它,Haskell是很难推理的——整个语言都是基于这个假设构建的

有了这个警告,这里有一些有趣的注意事项,因为我已经进入了这个特殊的兔子洞

在我看来,您正在对从ID到它们引用的对象的映射进行建模,并且该映射可以是多态的。也就是说,您可能需要以下功能:

insert :: SomeClass a => a -> MyMap -> (SomeId, MyMap)
lookup :: MyMap -> SomeId -> ???
有一些合适的值???这是不可能的,因为我们忘记了类型信息。我想你可以用:

lookup :: MyMap -> SomeId -> Maybe Dynamic
取决于你想做什么。这将涉及到大量的铸造,通过这样做,你基本上是把静态类型的安全抛出窗外

也许你想考虑类型化的标识符?< /P>

insert :: SomeClass a => a -> MyMap -> (SomeId a, MyMap)
lookup :: MyMap -> SomeId a -> Maybe a
如果您有一个类需要一些唯一的哈希生成函数,并且它确实保证了即使在不同类型之间也是唯一的,那么您可以使这个API工作。如果发生冲突,则会出现严重的不安全错误,例如,在ghci中尝试不安全42 foo::String。您可以通过在Id类型中添加一个来减轻一些痛苦,但是您仍然需要自己决定如何散列这些值

您可以使用unsafePerformIO将值与生成密钥时新生成的值配对,但如果这样做,将违反引用透明度,因为如果生成两个具有相同值的密钥,它们将比较不同,这在纯语言中是毫无意义的

基本上,异构地图的整个概念 困难重重

我写了一个实现了类型安全的异构映射的程序,它更像是一个艺术作品,讲述了这个想法是多么无聊,而不是一个有用的模块。但是,嘿,如果你觉得有用的话,请告诉我-


也许你应该退一步,描述一下你试图解决的更大的问题,这样你就会觉得你需要这个。我向你保证有更好的办法。

这太糟糕了,你绝对不应该这样做。如果GC决定移动对象,而GHC有一个压缩收集器AFAIK,则此处的unsafeCoerce可能会返回不同的值,这取决于表达式是否已被求值,因此这种情况将一直发生,甚至可能取决于通过读取到末尾与之无关的对象。糟糕,糟糕的主意,这个

引用相等测试,就像您在大多数命令式语言中看到的那样

var foo = Object();
var bar = Object();
foo === foo   // true
foo === bar   // false
在Haskell中是不可能的,因为引用的透明性,这意味着您总是可以用变量的定义替换变量,并且什么都不会改变。如果不应命名的语言具有引用透明性,则可以将该代码替换为:

 Object() === Object()  // true
 Object() === Object()  // false
这显然是一个矛盾。引用透明性是让Haskell如此好用的因素之一,但不幸的是,如果我推断你想做什么,这将使它变得不可能。我建议不要绕过引用透明性,使用unsafePerformIO可以做到这一点——没有它,Haskell是很难推理的——整个语言都是基于这个假设构建的

有了这个警告,这里有一些有趣的注意事项,因为我已经进入了这个特殊的兔子洞

在我看来,您正在对从ID到它们引用的对象的映射进行建模,并且该映射可以是多态的。也就是说,您可能需要以下功能:

insert :: SomeClass a => a -> MyMap -> (SomeId, MyMap)
lookup :: MyMap -> SomeId -> ???
有一些合适的值???这是不可能的,因为我们忘记了类型信息。我想你可以用:

lookup :: MyMap -> SomeId -> Maybe Dynamic
取决于你想做什么。这将涉及到大量的铸造,通过这样做,你基本上是把静态类型的安全抛出窗外

也许你想考虑类型化的标识符?< /P>

insert :: SomeClass a => a -> MyMap -> (SomeId a, MyMap)
lookup :: MyMap -> SomeId a -> Maybe a
如果您有一个类需要一些唯一的哈希生成函数,并且它确实保证了即使在不同类型之间也是唯一的,那么您可以使这个API工作。如果发生冲突,则会出现严重的不安全错误,例如,在ghci中尝试不安全42 foo::String。您可以通过在Id类型中添加一个来减轻一些痛苦,但是您仍然需要自己决定如何散列这些值

您可以使用unsafePerformIO将值与生成密钥时新生成的值配对,但如果这样做,将违反引用透明度,因为如果生成两个具有相同值的密钥,它们将比较不同,这在纯语言中是毫无意义的

基本上,异构地图的整个概念充满了困难

我写了一个实现了类型安全的异构映射的程序,它更像是一个艺术作品,讲述了这个想法是多么无聊,而不是一个有用的模块。但是,嘿,如果你觉得有用的话,请告诉我-


也许你应该退一步,描述一下你试图解决的更大的问题,这样你就会觉得你需要这个。我向您保证有更好的方法。

HashMap键不需要Ord,只需要一个映射。你到底想做什么?HashMap键不需要Ord,只需要一个映射。你到底想做什么?为什么我不考虑转发到类型的自己的情商我不知道…有时候我们太执着于某件事了hard@luqui我也去过那里另一方面,异性恋地图相当整洁。很好的使用排名2的类型。为什么我没有考虑转发到类型的自己的情商我不知道…有时候我们太执着于某件事了hard@luqui我也去过那里另一方面,异性恋地图相当整洁。很好地使用了排名2的类型。