Rascal 中代数数据类型的继承?
Inheritance for Algebraic Data Types in Rascal?
对,我在 Rascal 中有这样的数据类型:
data Type = Any() | Void() | Int() | Not(Type l) | And(set[Type] es) | Or(set[Type] es);
我想做的是像这样定义另一种类型:
data Primitive = Any() | Void() | Int();
然后可以做这样的事情:
Primitive p = Any();
Type d = p;
或者,例如,在简化 Type
时匹配 Primitive
。像这样:
public Type reduce(Not(Primitive p)) = p;
目前,我能看到的唯一解决方案是像这样针对每种情况扩展上述规则:
public Type reduce(Not(Any)) = Any();
public Type reduce(Not(Void)) = Void();
public Type reduce(Not(Int)) = Int();
我猜想有一种方法可以做到这一点,但我还没有想出...想法?
问得好。 Rascal 没有用户定义的子类型,数据类型的类型是名义上的。这在理论上回答了你的问题,那么它在实践中是如何工作的呢?
- 语法类型对数据类型的回答略有不同,所以下面是两个故事;
- 有许多不同的习语可以用来对数据结构的层次结构进行建模,为了简单起见,我们在这里只展示三个;
这是一种使用新功能扩展数据类型的方法,不涉及添加新类型,这会生成一个与您预期的模型过于近似的模型:
// first the primitive types are defined (I've added Not here to make a point later):
data Type = Any() | Void() | Int() | Not(Type l);
// then the extension is added (perhaps in a different module)
data Type = And(set[Type] es) | Or(set[Type] es);
// the second definition adds its alternatives also to the child of `Not`.
第二种方式更接近实际扩展,因为原来的Type
没有扩展,也没有意外添加"junk":
// we give the original type a unique name:
data Primitive = Any() | Void() | Int();
// For the extension the Primitive type is not polluted with the new constructors, but
// it was wrapped inside a singleton constructor `prim`
data Type = prim(Primitive super) | And(set[Type] es) | Or(set[Type] es);
当然,第二个解决方案会让您在可能的模式匹配中添加 prim
构造函数,但 /
深度匹配运算符将允许您尽可能忽略它。例如:
bool evalt(prim(p)) = evalp(p);
bool evalp(Any()) = true;
bool evalp(Not(p)) = !evalp(p);
bool containsVoid(Type t) = /Void() := t;
语法类型的情况类似,但由于语法类型中的链式规则是不可见的,因此它增加了一些额外的风味:
syntax Primitive = "any" | "void" | "int";
// notice the first chain rule or "injection" of Primitive into Type:
syntax Type = Primitive | left Type "∧" Type > left Type "∨" Type;
bool evalt((Type) `any`) = true; // the chain rule is parsed but invisible
人们一直在讨论将隐式链接也添加到抽象数据类型中,因为这样模拟子类型很有吸引力。我想这就像 Scala 的隐式函数。陪审团仍然没有定论。
简短的回答:尽管抽象数据类型可以扩展(即它们的定义可以跨模块扩展),但没有直接继承.
解决方法:
解决方案 A
data Type = Any() | Void() | Int() | Not(Type l) | And(set[Type] es) | Or(set[Type] es);
bool isPrim(Any()) = true;
bool isPrim(Void()) = true;
bool isPrim(Int()) = true;
default bool isPrim(Type t) = false;
Type reduce(Not(Type t)) = t when isPrim(t);
default Type reduce(Type t ) = t;
此处 Type
的所有构造函数都在单个 ADT 中,谓词 isPrim
选择原语。例如,reduce(Not(Void()))
将减少到 Void()
。
方案B
data Primitive = Any() | Void() | Int();
data Type = prim(Primitive p) | Not(Type l) | And(set[Type] es) | Or(set[Type] es);
Type reduce(Not(prim(Primitive p))) = prim(p);
default Type reduce(Type t ) = t;
这里的基元被收集在一个单独的 ADT Primitive
中,它们通过构造函数 prim
包含在 Type
中。现在 reduce(Not(prim(Void())))
将减少到 prim(Void())
。
最后的笔记
- 我们也希望继承(没有额外的构造函数
prim
,如 解决方案 B),但由于各种技术原因,我们没有包含它。虽然令人向往,但我不确定我们是否会这样做。
- 注意前面有
default
的函数,它们是 catch all 函数的其他声明不匹配的情况。
- 所有函数都是
public
,除非前面有关键字private
。
对,我在 Rascal 中有这样的数据类型:
data Type = Any() | Void() | Int() | Not(Type l) | And(set[Type] es) | Or(set[Type] es);
我想做的是像这样定义另一种类型:
data Primitive = Any() | Void() | Int();
然后可以做这样的事情:
Primitive p = Any();
Type d = p;
或者,例如,在简化 Type
时匹配 Primitive
。像这样:
public Type reduce(Not(Primitive p)) = p;
目前,我能看到的唯一解决方案是像这样针对每种情况扩展上述规则:
public Type reduce(Not(Any)) = Any();
public Type reduce(Not(Void)) = Void();
public Type reduce(Not(Int)) = Int();
我猜想有一种方法可以做到这一点,但我还没有想出...想法?
问得好。 Rascal 没有用户定义的子类型,数据类型的类型是名义上的。这在理论上回答了你的问题,那么它在实践中是如何工作的呢?
- 语法类型对数据类型的回答略有不同,所以下面是两个故事;
- 有许多不同的习语可以用来对数据结构的层次结构进行建模,为了简单起见,我们在这里只展示三个;
这是一种使用新功能扩展数据类型的方法,不涉及添加新类型,这会生成一个与您预期的模型过于近似的模型:
// first the primitive types are defined (I've added Not here to make a point later):
data Type = Any() | Void() | Int() | Not(Type l);
// then the extension is added (perhaps in a different module)
data Type = And(set[Type] es) | Or(set[Type] es);
// the second definition adds its alternatives also to the child of `Not`.
第二种方式更接近实际扩展,因为原来的Type
没有扩展,也没有意外添加"junk":
// we give the original type a unique name:
data Primitive = Any() | Void() | Int();
// For the extension the Primitive type is not polluted with the new constructors, but
// it was wrapped inside a singleton constructor `prim`
data Type = prim(Primitive super) | And(set[Type] es) | Or(set[Type] es);
当然,第二个解决方案会让您在可能的模式匹配中添加 prim
构造函数,但 /
深度匹配运算符将允许您尽可能忽略它。例如:
bool evalt(prim(p)) = evalp(p);
bool evalp(Any()) = true;
bool evalp(Not(p)) = !evalp(p);
bool containsVoid(Type t) = /Void() := t;
语法类型的情况类似,但由于语法类型中的链式规则是不可见的,因此它增加了一些额外的风味:
syntax Primitive = "any" | "void" | "int";
// notice the first chain rule or "injection" of Primitive into Type:
syntax Type = Primitive | left Type "∧" Type > left Type "∨" Type;
bool evalt((Type) `any`) = true; // the chain rule is parsed but invisible
人们一直在讨论将隐式链接也添加到抽象数据类型中,因为这样模拟子类型很有吸引力。我想这就像 Scala 的隐式函数。陪审团仍然没有定论。
简短的回答:尽管抽象数据类型可以扩展(即它们的定义可以跨模块扩展),但没有直接继承.
解决方法:
解决方案 A
data Type = Any() | Void() | Int() | Not(Type l) | And(set[Type] es) | Or(set[Type] es);
bool isPrim(Any()) = true;
bool isPrim(Void()) = true;
bool isPrim(Int()) = true;
default bool isPrim(Type t) = false;
Type reduce(Not(Type t)) = t when isPrim(t);
default Type reduce(Type t ) = t;
此处 Type
的所有构造函数都在单个 ADT 中,谓词 isPrim
选择原语。例如,reduce(Not(Void()))
将减少到 Void()
。
方案B
data Primitive = Any() | Void() | Int();
data Type = prim(Primitive p) | Not(Type l) | And(set[Type] es) | Or(set[Type] es);
Type reduce(Not(prim(Primitive p))) = prim(p);
default Type reduce(Type t ) = t;
这里的基元被收集在一个单独的 ADT Primitive
中,它们通过构造函数 prim
包含在 Type
中。现在 reduce(Not(prim(Void())))
将减少到 prim(Void())
。
最后的笔记
- 我们也希望继承(没有额外的构造函数
prim
,如 解决方案 B),但由于各种技术原因,我们没有包含它。虽然令人向往,但我不确定我们是否会这样做。 - 注意前面有
default
的函数,它们是 catch all 函数的其他声明不匹配的情况。 - 所有函数都是
public
,除非前面有关键字private
。