元组,还是不是?

发布者:    |       Ceylon

很多人问Ceylon是否会有元组。嗯,我想,为什么不呢?编写以下通用代数数据类型很容易

shared interface TupleOrUnit<P...> 
        of Tuple<P...> | unit {}  //note: depends upon support for GADTs
shared class Tuple<T,P...>(T head, TupleOrUnit<P...> tail) 
        satisfies TupleOrUnit<T,P...> {
    shared T head = head;
    shared TupleOrUnit<P...> tail = tail;
}
shared object unit 
        extends Case("unit") 
        satisfies TupleOrUnit<> {}

请注意,这里TupleOrUnit的定义依赖于编译器对通用代数数据类型(GADTs)的支持,因为子句中的类型不涵盖P...的每一个可能的参数列表...。我们正在尝试让编译器推断出Tuple.tail的类型是Tuple当有多个类型参数时。即使我们没有对GADTs的支持,我们也可以通过删除子句中的类型不涵盖P...子句,并添加一个引入,来获得元组支持。这是一个稍微有些丑陋的解决方案,但不会影响客户端。无论如何,代码最终看起来像这样TupleOrUnit无论如何,无论我们选择哪条路径,现在你都可以创建一个这样的元组

shared interface TupleOrUnit<P...> {}
shared class Tuple<T,P...>(T head, TupleOrUnit<P...> tail) 
        satisfies TupleOrUnit<T,P...> {
    shared T head = head;
    shared TupleOrUnit<P...> rest = tail;
}
shared object unit satisfies TupleOrUnit<> {}
shared interface TupleTail 
        adapts Tuple<T,P...> {
    shared Tuple<P...> tail { 
        if (is Tuple<P...> rest) {
            return rest;
        }
        else {
            //someone else extended TupleOrUnit directly
            throw Exception("unexpected subtype of TupleOrUnit");
        }
    }
}

并且可以像这样访问它的元素

local labeledPosition = Tuple(x,Tuple(y,Tuple(label,unit)));  //inferred type Tuple<Float,Float,String>

嗯,这有点啰嗦,所以我们可能需要添加一些方便的函数来处理成对和三联组

Float x = labeledPosition.head;
Float y = labeledPosition.tail.head;
String name = labeledPosition.tail.tail.head;

这让我们可以将上面的示例简化为

shared Tuple<X,Y> pair<X,Y>(X x, Y y) { return Tuple(x,Tuple(y,unit)); }
shared Tuple<X,Y,Z> triple<X,Y,Z>(X x, Y y, Z z) { return Tuple(x, pair(y,z)); }

shared T first<T,P...>(Tuple<T,P...> tuple) { return tuple.head; }
shared T second<S,T,P...>(Tuple<S,T,P...> tuple) { return tuple.tail.head; }
shared T third<R,S,T,P...>(Tuple<R,S,T,P...> tuple) { return tuple.tail.tail.head; }

现在,这一切都很合理,也许我们可以将此类代码作为语言模块的一部分提供,但事实是,这并不是人们所要求的。他们想要的是。为了使元组足够方便而值得使用,我认为你真的需要在语言定义中对它们提供一些额外的语法支持

local labeledPosition = triple(x,y,label);
Float x = first(labeledPosition);
Float y = second(labeledPosition);
String name = third(labeledPosition);

使用简化语法实例化它们的更方便的方法

  • 在方法参数列表、循环中以及可能甚至在规范语句的左侧支持解构元组
    local labeledPosition = (x,y,label);
  • 编写元组类型的缩写语法......
    for ((Float x, Float y, String label) in labeledPositions) { ... }
    (Float x, Float y, String label) = labeledPosition;
  • ...
    (Float,Float,String) labeledPosition;

嗯,这为语言功能添加了大量的额外语法,在我看来,在Ceylon这样的面向对象语言中,这个功能的价值相当有限。我的意思是,我可以看到一些合理的泛型对和三联组的用例,但我真的不认为我们应该鼓励人们编写返回4元组的代码。

对于表示某种关联数组的条目对的特殊情况,我们确实已经有了特殊的语法糖。条目,具有

  • 方便的->实例化它们的运算符,以及
  • 方法参数列表和...循环中的解构支持。

因此,我认为这个特性并不值得增加额外的复杂性。这仅仅是一个额外的学习点,而且这个特性很容易被误用。但是,我保留在未来某个时候改变看法的权利!


返回顶部