哪些静态类型的语言支持函数返回值的交集类型?


17

初步说明:

经过几次修改后,这个问题才得以解决,因为我缺乏适当的术语来准确说明我要寻找的内容。然后,Sam Tobin-Hochstadt发表了一条评论,这使我确切地知道了这是什么:支持函数返回值的交集类型的编程语言。

既然问题已经重新提出,我已经决定通过(希望)更精确的方式重写它来改进它。因此,下面的某些答案和注释可能不再有意义,因为它们是指先前的编辑。(在这种情况下,请参阅问题的编辑历史记录。)

是否有支持功能返回值的交集类型的流行的静态和强类型编程语言(例如Haskell,通用Java,C#,F#等)?如果是这样,那又如何?

(老实说,我真的很希望看到有人演示一种如何使用主流语言(例如C#或Java)来表示交集类型的方法。)

我将使用一些类似于C#的伪代码给出一个简短的示例,说明交集类型的外观:

interface IX { … }
interface IY { … }
interface IB { … }

class A : IX, IY { … }
class B : IX, IY, IB { … }

T fn()  where T : IX, IY
{
    return … ? new A()  
             : new B();
}

也就是说,该函数fn返回某种类型的实例,T调用者仅知道其实现了接口IX和的类型IY。(也就是说,与泛型不同,调用者没有选择T函数的具体类型,而是函数。T实际上,这不是通用类型,而是存在类型。)

PS:我知道可以简单地定义a interface IXY : IX, IY并将返回类型更改fnIXY。然而,这是不是真的同样的事情,因为通常你可以额外的接口上不螺栓IXY,以先前定义的类型A,其仅实现IXIY分开。


脚注:有关路口类型的一些资源:

维基百科上有关“类型系统”的文章有一个关于交叉点类型小节

Benjamin C. Pierce(1991)的报告,“使用交叉点类型,并集类型和多态性进行编程”

David P. Cunningham(2005),“实践中的交集类型”,其中包含有关Forsythe语言的案例研究,该文章在Wikipedia文章中提到。

一个堆栈溢出问题,“联合类型和交叉点类型”,得到了几个很好的答案,其中一个给出了类似于上面我的交叉点类型的伪代码示例。


6
这是如何模棱两可的?T定义一个类型,即使它只是在函数声明为“某种类型的扩展/工具中定义IXIY”。实际返回值是该返回值(AB分别)的特殊情况,在这里并没有什么特别的事实,您也可以使用Object代替代替来实现T
约阿希姆·绍尔

1
Ruby允许您从函数返回任何想要的东西。其他动态语言也一样。
thorstenmüller2012年

我已经更新了答案。@Joachim:我知道“歧义”一词不能非常准确地描述所涉及的概念,因此可以使用该示例来阐明预期的含义。
stakx 2012年

1
广告PS:...将您的问题更改为“ 在实现T接口的I所有方法但未声明该接口时,哪种语言允许将类型视为接口”。
Jan Hudec 2012年

6
结束这个问题是一个错误,因为有一个准确的答案,即并集类型。联合类型可用(Typed Racket)[ docs.racket-lang.org/ts-guide/]之类的语言提供。
山姆·托宾·霍斯塔特

Answers:


5

Scala在语言中内置了完整的交集类型:

trait IX {...}
trait IY {...}
trait IB {...}

class A() extends IX with IY {...}

class B() extends IX with IY with IB {...}

def fn(): IX with IY = if (...) new A() else new B()

基于dotty的scala将具有真实的交集类型,但不适用于当前/以前的scala。
陈洪绪

9

实际上,显而易见的答案是:Java

虽然您可能会惊讶地发现Java支持交集类型……它确实是通过“&”类型绑定运算符来实现的。例如:

<T extends IX & IY> T f() { ... }

这个链接在Java中的多个类型界限,也该从Java API。


如果您在编译时不知道类型,那行得通吗?即一个人可以写<T extends IX & IY> T f() { if(condition) return new A(); else return new B(); }。在这种情况下如何调用该函数?A和B都不会出现在呼叫站点,因为您不知道会得到哪一个。
Jan Hudec 2012年

是的,您是对的---它与给出的原始示例并不等效,因为您确实需要提供具体的类型。如果我们可以使用带有交界的通配符,那么就可以了。似乎我们不能...而且我真的不知道为什么不这样做(请参阅)。但是,Java仍然具有某种交集类型……
redjamjar 2012年

1
我很失望,因为我以Java编写的10年间以某种方式从未学习过交集类型。现在,我一直使用Flowtype,我认为它们是Java中最大的缺失功能,但发现我从来没有在野外见过它们。人们正在严重利用它们。我认为,如果更好地了解它们,那么像Spring这样的依赖注入框架将永远不会变得如此流行。
安迪

8

原始问题要求“歧义类型”。为此,答案是:

模棱两可的类型,显然没有。呼叫者需要知道他们会得到什么,所以这是不可能的。任何语言都可以返回的是基本类型,接口(可能是在交叉类型中自动生成的)或动态类型(动态类型基本上只是带有名称调用,获取和设置方法的类型)。

推断的接口:

所以基本上你希望它返回一个接口IXY是导出IX IY虽然该接口并没有在声明的任何AB可能是因为时定义这些类型未声明。在这种情况下:

  • 显然,任何动态输入的内容。
  • 我不记得任何静态类型语言的主流将能够生成接口(它是联合型的AB或交叉型IXIY)本身。
  • GO,因为如果类具有正确的方法,则它们无需声明就可以实现接口。因此,您只需要声明一个接口即可在此派生两个接口并返回它。
  • 显然,可以定义类型以实现该类型的定义之外的接口的任何其他语言,但是除了GO之外,我不记得任何其他语言。
  • 在必须在类型定义本身中定义实现接口的任何类型中,这都是不可能的。但是,您可以通过定义实现两个接口并将所有方法委托给包装对象的包装器来解决大多数情况。

PS类型语言是其中给定类型的一个对象不能被视为另一种类型的对象,而类型语言是一个具有重新解释铸造。因此,所有动态类型的语言都是强类型的,而弱类型的语言是汇编语言,C和C ++,这三种都是静态类型的。


这是不正确的,类型没有任何歧义。一种语言可以返回所谓的“交集类型” ---如果有主流语言的话,返回的类型就很少。
redjamjar 2012年

@redjamjar:当我回答该问题并询问“歧义类型”时,该问题的措词不同。这就是为什么从此开始。此后,这个问题被大大改写了。我将扩展答案以提及原始措词和当前措词。
Jan Hudec 2012年

抱歉,我很想念那个!
redjamjar 2012年

+1代表Golang,这可能是允许使用此语言的通用语言的最佳示例,即使这样做的方式有些circuit回。
Jules

3

转到编程语言 种类有这个,但仅限于接口类型。

在Go中,任何为其定义了正确方法的类型都会自动实现一个接口,因此PS中的异议不适用。换句话说,只需创建一个具有所有要合并的接口类型的所有操作的接口(语法很简单)就可以了。

一个例子:

package intersection

type (
    // The first component type.
    A interface {
        foo() int
    }
    // The second component type.
    B interface {
        bar()
    }

    // The intersection type.
    Intersection interface {
        A
        B
    }
)

// Function accepting an intersection type
func frob(x Intersection) {
    // You can directly call methods defined by A or B on Intersection.
    x.foo()
    x.bar()

    // Conversions work too.
    var a A = x
    var b B = x
    a.foo()
    b.bar()
}

// Syntax for a function returning an intersection type:
// (using an inline type definition to be closer to your suggested syntax)
func frob2() interface { A; B } {
    // return something
}

3

您可以通过使用有界的存在类型来执行所需的操作,该类型可以使用具有泛型和有界多态性的任何语言进行编码,例如C#。

返回类型将类似于(在伪代码中)

IAB = exists T. T where T : IA, IB

或在C#中:

interface IAB<IA, IB>
{
    R Apply<R>(IABFunc<R, IA, IB> f);
}

interface IABFunc<R, IA, IB>
{
    R Apply<T>(T t) where T : IA, IB;
}

class DefaultIAB<T, IA, IB> : IAB<IA, IB> where T : IA, IB 
{
    readonly T t;

    ...

    public R Apply<R>(IABFunc<R, IA, IB> f) {
        return f.Apply<T>(t);
    }
}

注意:我尚未对此进行测试。

问题的关键是,IAB必须能够申请的IABFunc任何返回类型R,并且IABFunc必须能够在任何工作T这两种亚型IAIB

的目的DefaultIAB只是包装现有的T哪些子类型IAIB。请注意,这与您的不同之处IAB : IA, IB在于,以后DefaultIAB可以随时将其添加到现有项T中。

参考文献:


如果添加了一个通用的对象包装器类型,并且参数T,IA,IB的T限制在接口中,则该方法有效,它封装了类型T的引用并允许Apply对其进行调用。最大的问题是,无法使用匿名函数来实现接口,因此像这样的构造最终会让人难以使用。
2014年

3

TypeScript是另一种支持交集类型T & U(以及并集类型T | U)的语言。这是从他们的文档页面引用的有关高级类型的示例:

function extend<T, U>(first: T, second: U): T & U {
    let result = <T & U>{};
    for (let id in first) {
        (<any>result)[id] = (<any>first)[id];
    }
    for (let id in second) {
        if (!result.hasOwnProperty(id)) {
            (<any>result)[id] = (<any>second)[id];
        }
    }
    return result;
}

2

锡兰完全支持一流的联合和相交类型

您将联合类型输入为X | Y,交集类型输入为X & Y

更好的是,锡兰针对这些类型提供了许多复杂的推理,包括:

  • 主要实例化:例如,Consumer<X>&Consumer<Y>与类型相同,就Consumer<X|Y>好像ConsumerX和中是相反的。
  • 不相交:例如,Object&Null是相同的类型Nothing,底部类型。

0

C ++函数都有固定的返回类型,但是如果它们返回指针,则指针可以在限制的情况下指向不同的类型。

例:

class Base {};
class Derived1: public Base {};
class Derived2: public Base{};

Base * function(int derived_type)
{
    if (derived_type == 1)
        return new Derived1;
    else
        return new Derived2;
}

返回的指针的行为将取决于virtual定义的函数,您可以使用例如

Base * foo = function(...);dynamic_cast<Derived1>(foo)

这就是多态在C ++中的工作方式。


当然,可以使用一个any或一种variant类型,例如boost提供的模板。因此,限制不会一直存在。
Deduplicator 2015年

但是,这并不是问题所要问的,它是一种用于指定返回类型同时扩展两个已标识超类的方法,即class Base1{}; class Base2{}; class Derived1 : public Base1, public Base2 {}; class Derived2 : public Base1, public Base2 {}...现在,我们可以指定哪种类型允许返回一个Derived1或一个Derived2都不返回Base1还是Base2直接?
Jules

-1

蟒蛇

它的类型非常非常强。

但是创建函数时不会声明类型,因此返回的对象是“模糊的”。

在您的特定问题中,更好的术语可能是“多态的”。这是Python中常见的用例是返回实现通用接口的变量类型。

def some_function( selector, *args, **kw ):
    if selector == 'this':
        return This( *args, **kw )
    else:
        return That( *args, **kw )

由于Python是强类型的,因此生成的对象将是This或的实例,That并且不能(轻松地)强制转换为其他类型的对象。


这是相当误导的。虽然对象的类型几乎是不可变的,但可以在类型之间轻松转换值。举个简单的例子。
James Youngman

1
@JamesYoungman:什么?所有语言都是如此。我见过的所有语言都有to_string左,右和居中转换。我完全没收到你的评论。你能详细说明吗?
S.Lott 2012年

我试图理解“ Python是强类型”的意思。也许我误解了您所说的“强类型”的含义。坦白说,Python很少有我会与强类型语言关联的特性。例如,它接受程序,其中函数的返回类型与调用者对值的使用不兼容。例如,“ x,y = F(z)”,其中F()返回(z,z,z)。
James Youngman

Python对象的类型不能更改(没有严重的魔术)。没有Java和C ++所具有的“ cast”运算符。这使得每个对象都是强类型的。变量名和函数名没有类型绑定,但是对象本身是强类型的。这里的关键概念不是声明的存在。关键概念是强制转换操作符。另请注意,在我看来,这是事实。主持人可能对此表示怀疑。
S.Lott 2012年

1
C和C ++强制转换操作也不会更改其操作数的类型。
James Youngman
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.