如何使用 Raku 类型系统强制执行不变性?

cod*_*ons 6 types hashmap typechecking raku

我欣赏不可变数据结构的价值,真的很喜欢 Raku 内置了很多。我特别喜欢编译器/类型检查器会为我强制执行不变性——我可能有休息日或对某些事情粗心大意,但编译器永远不会.

或者至少,我是这么想的。

然而,我很惊讶地看到以下代码在没有从类型检查器中窥视的情况下运行:

my Map $m = Hash.new('key', 'value');
say $m.WHAT # OUTPUT: «(Hash)»
Run Code Online (Sandbox Code Playgroud)

咨询文件后,我看到Map是一个父类Hash(因此Hash.isa('Map')回报True所以,我明白了。如何(在机械水平),其typechecks成功,但我留下了两个问题:第一,为什么不继承工作这样的, , 其次,如果我真的希望类型检查器保证我的不可变变量保持这种状态,我该怎么办。

关于“为什么”的问题——像这样构建的地图有什么不同?乐的其他稳定的类型都不是:Set.isa('SetHash')Mix.isa('MixHash')Bag.isa('BagHash')Blob.isa('Buf'),和(如果计算)List.isa('Array')所有返回False。[编辑:正如jjmerelo在下面指出的那样,我颠倒了所有这些。我应该说SetHash.isa('Set')MixHash.isa('Mix')BagHash.isa('Bag')Buf.isa('Blob')所有的回报False。有趣的是,Array.isa('List')returnsTrue为 Elizabeth Mattijsen 的声明提供了一些支持,即这是一个历史疏忽——sListMaps 绝对是比大多数其他不可变类型更基本的数据类型。]

什么是关于不同MapS和HashES,他们有这样的行为?

关于更实际的问题,我能做些什么来让类型检查器在这里帮助我更多吗?我知道,在这种特定情况下,我可以写一些类似的东西

my Map $m where { .WHAT === Map } = Hash.new('key', 0); # Throws the error I wanted
Run Code Online (Sandbox Code Playgroud)

甚至

subset MapForRealThisTime of Map where { .WHAT === Map }
Run Code Online (Sandbox Code Playgroud)

这些真的是最好的选择吗?他们都觉得有点笨拙(并且where块可能有运行时成本?)但也许这是最好的方法?

更一般地说,我真正想要的是一种在严格模式下进行类型检查的方法,可以这么说。如果我显式声明一个变量的类型,我真的希望编译器保证该变量具有那个确切的类型——而不是其他一些恰好将该类型作为父类型的类型。有没有我可以采取的更通用的方法,或者我只是要求 Raku 不会提供的严格程度?

Eli*_*sen 5

Maps 和 Hashes 的这种行为有何不同?

就我个人而言,我认为这是一个历史性的疏忽,需要在未来的某个时候修复。

这些真的是最好的选择吗?

我认为您正在寻找is这种情况下的特征:

my %m is Map = a => 42, b => 666;
dd %m;  # Map.new((:a(42),:b(666)))
%m<a> = 666; # Cannot change key 'a' in an immutable Map
%m<c> = 666; # Cannot add key 'c' to an immutable Map
Run Code Online (Sandbox Code Playgroud)

我只是要求 Raku 不会提供的严格程度吗

恐怕你是。=:=您可以在子句中使用运算where符:

subset RealMap of Map where .WHAT =:= Map;
my RealMap $m = Map.new((a => 42)); # works
my RealMap $h = Hash.new((a => 42));
# Type check failed in assignment to $m; expected RealMap but got Hash ({:a(42)})
Run Code Online (Sandbox Code Playgroud)