用Haskell表示逻辑常数

用Haskell表示逻辑常数,haskell,data-structures,constants,Haskell,Data Structures,Constants,数据结构具有变异和非变异操作。例如,字典插入可以更改其基础数据结构的状态,但查找通常不会 有些数据结构会改变其内部结构——即使是在逻辑上不发生变化的操作上——但其方式不会改变可观察状态。例如,a在查找时将元素移向根,a在查找时将元素移向头。从逻辑上讲,键集不会因此而改变 在C++中,这可以通过定义一个具有 const 方法的数据结构来传递,但数据成员除外。哈斯克尔有没有办法做到这一点?我唯一能想到的就是 setContains :: Set k -> k -> (Set k, Boo

数据结构具有变异和非变异操作。例如,字典插入可以更改其基础数据结构的状态,但查找通常不会

有些数据结构会改变其内部结构——即使是在逻辑上不发生变化的操作上——但其方式不会改变可观察状态。例如,a在查找时将元素移向根,a在查找时将元素移向头。从逻辑上讲,键集不会因此而改变

在C++中,这可以通过定义一个具有<代码> const 方法的数据结构来传递,但数据成员除外。哈斯克尔有没有办法做到这一点?我唯一能想到的就是

setContains :: Set k -> k -> (Set k, Bool)

但是这很难看,因为底层数据结构改变了接口。

如果不使用不安全的原语,就无法实现这种低级优化,从而允许从纯代码中变异数据结构

首先,请注意,在GHC运行时中,纯代码确实会修改数据结构——通过计算它们。例如

x = (3+2, 4+5)
main = print (fst x) >>> print (fst x)
在GHC中,第一个
print
调用实际上将
x
重写为
(5,4+5)
,并用结果重写其第一个组件。这样,第二次
打印
不必再次执行添加。 当然,这种重写永远不会改变
x
的语义,因此它是一种特殊的“安全”突变

有时,这还不足以实现一些低级优化,如问题中描述的优化。因此,不安全的原语是唯一的选择

我相信这项技术的典范是。这是一个不可变的数组数据结构,具有恒定的访问和更新时间(!)。在引擎盖下,有一个可变的数组,其中更新是在适当的位置执行的。由于这将可怕地破坏对不可变数组的旧引用,因此此类引用还指向一个(可变的)“changelog”,该“changelog”存储更新之前的旧值。因此,我们得到了一种“版本控制”系统:最后一个版本很快,而旧版本的访问速度越来越慢。 从外部看,只能观察到纯粹的行为;在内部,发生了很多突变

该实现使用
MVar
s来阻止并发访问,以避免OP在上面的注释中描述的多线程问题


让我再次强调,在哈斯凯尔,这种把戏肯定被认为是不符合实际的。在日常编程中,这不是编写好的、可读的,坚实的Haskell代码。

根据定义,Haskell中的一切都是不可变的,也许它很难看,但它是诚实的too@AmiTavory从哈斯克尔的角度来看,当你改变结构时,这是不诚实的——这是一件哲学的事情——一个MLer或F#er会同意你的观点——但你可以说它是可观察的(下次你会更快地找到答案)。。。请注意这些东西——虽然我可以看出这可能是一个很好的用例,但它很容易导致我们声称在Haskell中解决的所有问题;)@卡斯滕指出了一些有利于你的观点,在C++中,通过线程访问这些类型的容器时会遇到一个巨大的问题:接口说它是代码> const ,但是当两个线程做(甚至只是)查找时,显然有数据竞争。如果你想在哈斯克尔中实现一个根本可变的数据结构,你应该给它一个可变的接口。这样,您就不用担心破坏Haskell的语义。这并不意味着您必须要求用户手动跟踪哪个集合是哪个集合-您可以编写,例如
setContains::set k->k->IO Bool
DiffArray
早已过时。。它不在任何最新版本的
array
array-0.2.0.0
,包含它的最后一个版本,已经有8年(!)了。@user2407038 True,但是我找不到更好的例子。我提到它更多的是为了理念而不是实践。如果你知道现代“纯界面/内部易变性”的例子,请随意推荐一个。Ed Kmett在过去一年左右一直在研究一些。可以在可变和不变形式之间廉价切换的奇怪结构。