어떤 정적 유형 언어는 함수 반환 값에 대한 교차 유형을 지원합니까?


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호출자가이 인터페이스를 구현하는 것으로 만 알고있는, IXIY. (즉, 제네릭과 달리 호출자는 구체적인 유형을 선택 T하지 못합니다. 함수는 T실제로 이것을 범용 유형이 아니라 실존 유형 이라고 가정합니다 .)

추신 : 나는 단순히 a를 정의하고 interface IXY : IX, IY의 반환 유형을 fn로 변경할 수 있음을 알고 있습니다 IXY. 그러나 추가 인터페이스 IXY를 이전에 정의 된 유형 A으로 만 구현 IX하고 IY별도로 구현할 수없는 경우가 종종 있기 때문에 이는 실제로 동일하지 않습니다 .


각주 : 교차로 유형에 대한 일부 리소스 :

"유형 시스템"에 대한 Wikipedia 기사 에는 교차 유형에 대한 하위 섹션이 있습니다 .

Benjamin C. Pierce (1991), "교차 유형, 조합 유형 및 다형성 프로그래밍"

David P. Cunningham (2005), "실제로 교차로 유형" .이 문서에는 Wikipedia 기사에 언급 된 Forsythe 언어에 대한 사례 연구가 포함되어 있습니다.

스택 오버플로 질문, "연합 유형과 교차 유형" 그 중 몇 가지 좋은 답변을 가지고 이 하나 위의 광산과 유사한 교차로 유형의 의사 코드 예제를 제공합니다.


6
이것은 어떻게 모호합니까? T그것은 단지 "/ 구현 확장 몇 가지 유형으로 함수 선언에서 정의되어 있더라도 유형을 정의 IXIY". 사실 실제 반환 값은 그 특별한 경우이다 ( A또는 B각각)는 단지뿐만 아니라 사용하여 해당를 얻을 수있는, 여기 아무것도 특별하지 않다 Object대신 T.
Joachim Sauer 2012

1
루비를 사용하면 함수에서 원하는 것을 반환 할 수 있습니다. 다른 동적 언어에서도 마찬가지입니다.
thorsten müller

내 답변을 업데이트했습니다. @Joachim : "ambigous"라는 용어가 문제의 개념을 매우 정확하게 포착하지 못하므로 의도 된 의미를 명확히하는 예를 알고 있습니다.
stakx 2019

1
Ad PS : ... 질문을 "어떤 유형의 언어 T가 인터페이스의 I모든 메소드를 구현할 때 인터페이스로 취급 할 수 있지만 해당 인터페이스를 선언하지 않은 경우"로 변경됩니다.
Jan Hudec

6
인, 정확한 답이 있기 때문에이 질문을 닫 실수였다 조합 유형 . 유니온 유형은 (Typed Racket) [ docs.racket-lang.org/ts-guide/] 와 같은 언어로 제공됩니다 .
Sam Tobin-Hochstadt

답변:


5

스칼라는 언어에 완전한 교차 유형을 가지고 있습니다 :

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()

도티 기반 스칼라는 실제 교차 유형을 갖지만 현재 / 이전 스칼라에는 적용되지 않습니다.
Hongxu Chen

9

사실, 확실한 대답은 다음과 같습니다. Java

Java가 교차 유형을 지원한다는 사실을 알고 놀랄 지 모르지만 실제로는 "&"유형 바운드 연산자를 통해 수행됩니다. 예를 들면 다음과 같습니다.

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

참조 링크 자바에서 여러 유형의 경계에, 그리고 자바 API에서.


컴파일 타임에 유형을 모르면 작동합니까? 즉 하나 쓸 수 있습니다 <T extends IX & IY> T f() { if(condition) return new A(); else return new B(); }. 그런 경우 어떻게 함수를 호출합니까? 어떤 사이트를 얻을지 모르기 때문에 A 나 B는 콜 사이트에 나타나지 않습니다.
Jan Hudec

예, 당신은 옳습니다 --- 구체적인 유형을 제공해야하기 때문에 주어진 원래 예와 동일하지 않습니다. 교차 경계와 함께 와일드 카드를 사용할 수 있다면 그렇게 할 수 있습니다. 우리가 할 수없는 것 같습니다 ... 왜 그런지 알지 못합니다 ( 이것을보십시오 ). 그러나 여전히 Java에는 일종의 교차 유형이 있습니다.
redjamjar

1
Java를 한 10 년 동안 교차 유형에 대해 전혀 배우지 못했다는 것에 실망합니다. 이제 Flowtype을 항상 사용하기 때문에 Java에서 가장 큰 기능이 누락되었다고 생각했습니다. 사람들이 심각하게 활용하고 있지 않습니다. 나는 그들이 더 잘 알려졌다면 Spring과 같은 의존성 주입 프레임 워크는 그렇게 인기가 없었을 것이라고 생각합니다.
Andy

8

원래 질문은 "모호한 유형"을 요구했습니다. 그 대답은 다음과 같습니다.

모호한 유형, 분명히 없음. 발신자는 무엇을 얻을 수 있는지 알아야하므로 불가능합니다. 리턴 할 수있는 모든 언어는 기본 유형, 인터페이스 (교차 유형과 같이 자동 생성 될 수 있음) 또는 동적 유형 (동적 유형은 기본적으로 이름 별 호출, get 및 set 메소드를 사용한 유형)입니다.

유추 된 인터페이스 :

그래서 기본적으로 당신이 인터페이스를 반환 할 IXY것을 도출 IX IY 해당 인터페이스가없는 하나에서 선언했지만 A또는 B그 유형이 정의 될 때 선언되지 않은 가능성이 있기 때문이다. 이 경우 :

  • 분명히 동적으로 입력되는 모든 것.
  • 내가 어떤 정적으로 입력 된 주류 언어 인터페이스를 생성 할 수있을 것 기억하지 않습니다 (그것은이다 노동 조합 의 유형 AB또는 교차로의 유형 IXIY자체).
  • GO 는 클래스가 선언하지 않고 올바른 메소드가있는 경우 인터페이스를 구현하므로 클래스입니다. 따라서 두 가지를 파생시키는 인터페이스를 선언하고 반환합니다.
  • 분명히 유형이 정의되지 않은 인터페이스를 구현하기 위해 유형을 정의 할 수있는 다른 언어이지만 GO 이외의 다른 언어는 생각하지 않습니다.
  • 유형 정의 자체에서 인터페이스 구현을 정의해야하는 유형에서는 불가능합니다. 그러나 두 인터페이스를 구현하고 모든 메소드를 랩핑 된 오브젝트에 위임하는 랩퍼를 정의하여 대부분의 문제를 해결할 수 있습니다.

PS 강력하게 형식화 된 언어는 동안 지정된 유형의 객체가 다른 유형의 객체로 취급 할 수없는 일이다 약하게 타입 언어가 재 해석 캐스트를 가지고 하나입니다. 따라서 동적으로 유형이 지정된 모든 언어는 강력하게 유형이 지정 되고 약한 유형의 언어는 어셈블리, C 및 C ++로 정적으로 유형이 지정 됩니다.


유형에 대해 모호한 것이없는 것은 맞지 않습니다. 언어는 소위 "교차 유형"을 반환 할 수 있습니다. 주류 언어가 그렇게하는 경우에는 그 수가 적습니다.
redjamjar

@ redjamjar : 질문에 대답하고 "모호한 유형"을 요구했을 때 질문에 다른 표현이있었습니다. 그것이 그로 시작하는 이유입니다. 그 이후로 질문은 크게 재 작성되었습니다. 나는 원래의 단어와 현재 단어를 모두 언급하기 위해 답을 확장 할 것이다.
Jan Hudec

미안, 나는 분명히 그것을 놓쳤다!
redjamjar

Golang을 언급하면 ​​+1인데, 언어를 사용하는 방법이 조금 회로적인 경우에도이를 허용하는 공통 언어의 가장 좋은 예일 것입니다.
Jules

3

Go Programming Language 종류는 인터페이스 유형에만 적용됩니다.

Go에서 올바른 방법이 정의 된 모든 유형은 자동으로 인터페이스를 구현하므로 PS의 반대 의견이 적용되지 않습니다. 다시 말해, 인터페이스 유형의 모든 작업이 결합 된 (간단한 구문이있는) 인터페이스와 모든 Just Works가있는 인터페이스를 만들면됩니다.

예를 들면 :

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 #과 같이 제네릭과 경계 다형성을 사용하는 모든 언어로 인코딩 할 수 있습니다.

리턴 타입은 (psuedo 코드에서)

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있다는 점 에서 다릅니다 .DefaultIABT

참고 문헌 :


이 접근 방식은 매개 변수 T, IA, IB를 가진 일반 객체 래퍼 유형을 인터페이스에 제한하여 T 유형의 참조를 캡슐화하고 Apply호출 할 수있는 경우에 작동합니다. 가장 큰 문제는 익명 함수를 사용하여 인터페이스를 구현할 수있는 방법이 없기 때문에 그러한 구조는 사용하기가 매우 어렵다는 것입니다.
supercat

3

TypeScriptT & 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합니다.

또한 Ceylon은 다음을 포함하여 이러한 유형에 대한 많은 정교한 추론을 제공합니다.

  • 주 인스턴스화 : 예를 들어, Consumer<X>&Consumer<Y>동일한 타입이 같은 Consumer<X|Y>경우 Consumercontravariant에있는 X, 및
  • 불연속성 : 예를 들어 하단 유형 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유형을 사용할 수 있습니다 . 따라서 제한이 유지되지 않습니다.
중복 제거기

이 문제는 즉, 방법은 반환 형식은 동시에 두 개의 확인 된 슈퍼 클래스를 확장하도록 지정하는이었다, 그러나, 요구 된 아주 것이 아니다 class Base1{}; class Base2{}; class Derived1 : public Base1, public Base2 {}; class Derived2 : public Base1, public Base2 {}... 지금은 어떤 종류의 우리가 그 반환하거나 수를 지정할 수 있습니다 Derived1또는 Derived2만도 Base1Base2직접?
Jules

-1

파이썬

매우 강력하게 타이핑되었습니다.

그러나 함수가 생성 될 때 형식이 선언되지 않으므로 반환 된 개체는 "모호합니다".

특정 질문에서 더 나은 용어는 "다형성"일 수 있습니다. 파이썬에서 일반적인 사용 사례는 공통 인터페이스를 구현하는 변형 유형을 반환하는 것입니다.

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

파이썬이 강력하게 형식화되어 있기 때문에, 결과 객체의 인스턴스가 될 것입니다 This또는 That과 (쉽게) 강제 또는 개체의 다른 형식으로 캐스팅 할 수 없습니다.


이것은 오해의 소지가 있습니다. 객체의 유형은 거의 변경되지 않지만 값은 유형간에 매우 쉽게 변환 될 수 있습니다. 예를 들어 사소하게 str.
James Youngman

1
@JamesYoungman : 무엇? 그것은 모든 언어에 해당됩니다. 내가 본 모든 언어에는 왼쪽, 오른쪽 및 중앙에서 to_string 변환이 있습니다. 나는 당신의 의견을 전혀 얻지 못했습니다. 정교하게 할 수 있습니까?
S.Lott

"Python is strong typed"의 의미를 이해하려고했습니다. 아마도 나는 당신이 "강하게 입력했다"는 말의 의미를 잘못 이해했을 것입니다. 솔직히 파이썬에는 강력한 형식의 언어와 연관시킬 특성이 거의 없습니다. 예를 들어, 함수의 리턴 유형이 호출자의 값 사용과 호환되지 않는 프로그램을 승인합니다. 예를 들어, "x, y = F (z)"에서 F ()는 (z, z, z)를 반환합니다.
James Youngman

파이썬 객체의 유형은 (심각한 마법없이) 변경할 수 없습니다. Java 및 C ++에는 "캐스트"연산자가 없습니다. 그러면 각 개체가 강력하게 형식화됩니다. 변수 이름과 함수 이름에는 형식 바인딩이 없지만 개체 자체는 강력하게 형식화됩니다. 여기서 핵심 개념은 선언이 없다는 것입니다. 핵심 개념은 캐스트 운영자가 사용할 수 있다는 것입니다. 또한 이것이 사실 인 것 같습니다. 그러나 중재자는 이의를 제기 할 수 있습니다.
S.Lott

1
C 및 C ++ 캐스트 연산은 피연산자의 유형도 변경하지 않습니다.
James Youngman
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.