d允许受控更改不变
原文
草稿
__metadata名字更好.是对象的一部分但需要从类型系统中留出一定余地的信息.
典型的用例是:不变数据结构的引用计数,缓存,懒求值.
陷入了__mutable与const父对象的交互(不清楚父对象是不变的还是不合格的),及纯函数应如何处理__mutable.
注意,DIP的当前状态与最初提案有很大不同.
为此,需要更通用的(最初用__mutable函数支持):手动分配和释放不变数据.
最初建议定义一组仅基于,如果没有__metadata(不变数据的可能引用标识除外)则保存语义的函数签名的如果有__metadata注解仍允许的重写.如果DIP太复杂,可简化为,允许重写不带__metadata的.
带__metadata注解的数据,不会放进只读内存.
我认为它不应是@safe.相反,pure和immutable应保存原义,表明用错__mutable了(因此是不安全的),即使有__mutable字段,仍然有非美观理由使用不变.
比如__gshared,只是为了谨慎使用.
如下,可见属性,行不?
struct something
{
metadata //限定符?
{
int a;
}
private
{
int b;
}
public
{
int c;
}
}
在对象中存储数据是重点.DIP动机是允许如引用计数等分配内存方案,及为"不变"限定数据实现懒初化.可变性是实现细节,从外部看,数据仍然"不变".(不过,当前版本DIP完全忽略了,泄漏__metadata到类型系统中.)编译器优化可改变在__metadata上代码语义.(如,如果它可不读懒初化字段,则不会初化该字段,如果消除引用副本,则不需更新引用计数等)
因此,修改__metadata,应与强纯重写一致.
不,关键是可从immutable和pure推导出的高级重写,不应受修改__metadata代码限制.
struct S{
private __metadata x;
}
void foo(immutable ref S s)pure{
s.x += 1;
}
void main(){
immutable S s;
foo(s); // 不用调用此函数
assert(s.x==1); // 不能依赖它,可能为0.
}
//-----
struct S{
private __mutable x;
}
int foo(immutable ref S s)pure{
s.x += 1;
return s.x;
}
void main(){
immutable S s;
int a=foo(s);
int b=foo(s); // 可重用先前结果
assert(a!=b); // 不能这样,a,b可能相同.
}
这正是引用计数的用例.有不变的可变部分的主要吸引力.
不,显然不是引用计数的用例.这里未计数引用.
数据结构不是(如删除引用副本)引用计数块优化.
为不变对象添加/删除引用时,它会传递不变对象给稍后会增加/减少__metadata字段(如上示例)的纯函数.如果删除1个,则清除引用计数.
我开始代码有__mutable概念.
不过,元数据概念可能会更强大,我认为它基本上与可变是正交的.
元数据不仅是私有的,而且可注解POD类外部数据.如,序化有metadata{}块变量对象不会(或不能)包含它们.
元数据{}块可能是放类/函数属性来避免混淆签名的好地方:
class Foo
{
metadata
{
synchronized;//移进类属性
scope;
@(someUDA);// UDA也放这
int secretSauce; //不论可变性,Foo可访问或修改
}
}
也非常适合合同和函数.正如不变对象中的元数据{}块允许修改成员变量,函数元数据{}块中的成员变量也可在调用纯函数中修改和持久化.就像静态变量,在编译时和运行时都可用,可能对调试,分析或基准测试有用.也许in&out合约块中代码可访问它,但禁止函数体这样.还需要考虑副作用.
int Bar(int a, int b)
metadata
{
pure: // 不确定
@nogc:// 强制@
@safe:
long startTicks;//基准测试
long endTicks;// 与函数无关
static int numTimesCalled; //纯函数
}
in
{
numTimesCalled++;//可修改元数据,没问题
...
}
do
{
if(numTimesCalled > 4)//这里非法,破坏了纯
...
}
当然,我只是建议单独保存引用计数或其他元数据是可能解决方法,只要有简单方法可取给定对象的元数据(在内存中,无论是否与对象其余数据一起).
单独存储元数据成员变量是实现细节.对该类,元数据与普通变量差不多,但强制转换为void*或序化时去掉元数据,因此可安全保存引用计数等.该方案中,可不改变内存布局的添加元数据到类或结构中,只要类型系统区别对待,元数据就有特殊威力,因为是在对象内存"外部"存储,而不是类型系统的特例.
所以__mutable函数不是强纯的.
只有不变结构才有共享元数据.普通可变结构可有线程本地元数据.所以这不对.
那样,为何加"不变"?你的语义对引用计数或缓存/懒求值有意义?
语义与编译器实现不同.具有__mutable字段数据不能放入只读内存的原因是不能在写入时崩溃.
加__keyword成本约为零.这只是肤浅的语法问题,因为a)当前版本DIP语义错误,b)更改语言规范成本小,类似加__keyword或pragma.(检查类型与注解语法无关.)
有不安全操作,也有无效操作.DIP目的是使一些以前无效案例不安全.(而且,如果必须为字段实现注解,则加几行前端代码来避免额外转换,不难)
pragma就够了,noROM名太难听了.
:肯定破坏不变性了
不,就像说Haskell不支持不变数据,因为只能用可变实现懒求值.(或更糟,声称D不支持不变性,因为垃集可回收类型为不变的内存.)
我认为D需要可实现自己的运行时库和手动内存分配,而无需用户程序无法使用的编译器神奇.
没有隐藏东西.
@system表明编写无定义语义程序时,你不受保护.很合适.
最简单方法是添加__metadata并简单添加编译器检查,确保用户代码无法访问它.
使用DIP,可取定义行为.
:关于"纯"函数编译器.我觉得是优化障碍.
相反,它是你为了抽象改变__元数据代码,而需要的类型系统信息.因为即使是"纯的"且不返回值,你也不能忽略赋值__metadata字段.对语句,需要可赋值有效类型给包含该语句函数,并取类型系统了解的该语句信息.
:考虑到目前编译器无基于纯的优化
需要指定,即使更改了程序可变集,也允许的优化.如前建议,如想最少限制语义,需要基于函数签名允许的优化,而不是"编译器当前不优化".如果想保守点,那恰恰是错误方向.
编译器做不做什么与语言语义代码有效性无关.
:使用__mutable注解deallocate可解决编译器优化问题.
简直是胡说八道.你定义语言语义,编译器程序员找出与该语义一致优化.优化不需要DIP,DIP用来指定程序行为.不能按不确定方式运行程序.
:而且,这是想要前进方向吗?
绝对地.如果想使用__metadata,你要知道可消除纯函数,如果函数中改变__metadata不是自包含的,因此可消除,你需要注解该函数.
__元数据的用户.各种优化.
然后放弃__metadata,并在没有不变限定符,就实现持久数据结构!不变如果不能证明纯函数上等价推理是,则无意义.
则这些访问不能是纯的,就像访问全局变量.
如果希望可在纯函数中访问__mutable字段,则1和2基本上是矛盾的,因为无法安全地检查2.
你需要定义语义!
除非不变是更高级抽象,显然不能更改不变对象字段.必须定义该抽象.一种定义__metadata语义,是显式给出允许重写集,但也可隐式定义它.
它不应.
一切都归结为想要用pure做什么.
浙公网安备 33010602011771号