타입 추론이 유용한 이유는 무엇입니까?


37

코드를 작성하는 것보다 코드를 더 자주 읽으며 산업용 소프트웨어를 사용하는 대부분의 프로그래머 가이 작업을 수행한다고 가정합니다. 내가 생각하는 형식 유추의 장점은 덜 장황하고 작성된 코드가 적다는 것입니다. 그러나 반면에 코드를 더 자주 읽는다면 읽을 수있는 코드가 필요할 것입니다.

컴파일러는 형식을 유추합니다. 이것에 대한 오래된 알고리즘이 있습니다. 그러나 실제 질문은 프로그래머가 코드를 읽을 때 변수 유형을 추론하려는 이유는 무엇입니까? 유형이 무엇인지 생각하는 것보다 유형을 읽는 것이 더 빠르지 않습니까?

편집 : 결론적으로 그것이 왜 유용한 지 이해합니다. 그러나 언어 기능 범주에서 연산자 과부하가있는 버킷에서 볼 수 있습니다. 경우에 따라 유용하지만 악용되면 가독성에 영향을 미칩니다.


5
내 경험에 따르면 코드를 읽는 것보다 코드를 작성할 때 유형이 훨씬 더 중요 합니다. 코드를 읽을 때 잘 명명 된 변수가 일반적으로 지시하는 알고리즘과 특정 블록을 찾고 있습니다. 정말 잘못 작성되지 않은 경우 수행중인 작업을 읽고 이해하기 위해 검사 코드를 입력 할 필요가 없습니다. 그러나 너무 많은 유형 주석과 같이 찾고 싶지 않은 불필요한 세부 사항이있는 코드를 읽을 때 종종 찾고있는 비트를 찾기가 더 어려워집니다. 필자가 말한 타입 추론은 코드 작성보다 훨씬 더 많은 것을 읽는 데 큰 도움이됩니다.
Jimmy Hoffa

내가 찾고있는 코드를 찾으면 유형 검사를 시작할 수 있지만 주어진 시간에 10 줄 이상에 집중해서는 안됩니다.이 시점에서 코드를 작성하는 것이 번거롭지 않습니다 처음부터 전체 블록을 정신적으로 선택하고 툴링을 사용하여 어쨌든 그렇게 할 수 있기 때문에 스스로를 추론하십시오. 영역 화하려는 10 줄의 코드 유형을 파악하는 데는 시간이 거의 걸리지 않지만 어쨌든 읽기에서 쓰기로 전환하는 부분입니다.
Jimmy Hoffa

비록 기억 프로그래머가 쓰기 그것보다 더 자주 코드를 읽고는 것을 의미하지 않는다 코드 조각이 더 자주 기록보다 읽습니다. 많은 코드는 수명이 짧거나 다시는 읽지 않을 수 있으며, 어떤 코드가 생존 할 수 있는지를 쉽게 알 수 없으며 최대 가독성으로 작성해야합니다.
jpa

2
@JimmyHoffa의 첫 번째 요점을 자세히 설명하면서 일반적인 독서를 고려하십시오. 개별 단어의 품사에 초점을 맞출 때 문장을 이해하고 해석하기가 더 쉬워 집니까? "(기사) 소 (명사-단수)가 (기사) 달 (명사). (마침표) 위로 (동시 과거) 뛰어 내 렸습니다."
Zev Spitz

답변:


46

Java를 살펴 보자. Java는 유추 된 유형의 변수를 가질 수 없습니다. 이것은 타입이 무엇인지 인간 독자에게 완전히 명백하더라도, 타입을 자주 철자해야 함을 의미합니다.

int x = 42;  // yes I see it's an int, because it's a bloody integer literal!

// Why the hell do I have to spell the name twice?
SomeObjectFactory<OtherObject> obj = new SomeObjectFactory<>();

때로는 전체 유형을 설명하는 것이 성가시다.

// this code walks through all entries in an "(int, int) -> SomeObject" table
// represented as two nested maps
// Why are there more types than actual code?
for (Map.Entry<Integer, Map<Integer, SomeObject<SomeObject, T>>> row : table.entrySet()) {
    Integer rowKey = entry.getKey();
    Map<Integer, SomeObject<SomeObject, T>> rowValue = entry.getValue();
    for (Map.Entry<Integer, SomeObject<SomeObject, T>> col : rowValue.entrySet()) {
        Integer colKey = col.getKey();
        SomeObject<SomeObject, T> colValue = col.getValue();
        doSomethingWith<SomeObject<SomeObject, T>>(rowKey, colKey, colValue);
    }
}

이 장황한 정적 타이핑은 프로그래머를 방해합니다. 대부분의 타입 주석은 우리가 이미 알고있는 것에 대한 반복적 인 라인 필러, 내용이없는 역류입니다. 그러나 버그를 발견하는 데 실제로 도움이 될 수 있으므로 정적 타이핑을 좋아하므로 동적 타이핑을 사용하는 것이 항상 좋은 대답은 아닙니다. 유형 유추는 두 세계에서 가장 좋습니다. 관련이없는 유형은 생략 할 수 있지만 여전히 내 프로그램 (유형)이 체크 아웃되어 있는지 확인하십시오.

형식 유추는 로컬 변수에 실제로 유용하지만 명확하게 문서화해야하는 공개 API에는 사용해서는 안됩니다. 때로는 유형이 코드에서 일어나는 일을 이해하는 데 매우 중요합니다. 이런 경우 타입 추론에만 의존하는 것은 어리석은 일입니다.

형식 유추를 지원하는 많은 언어가 있습니다. 예를 들면 다음과 같습니다.

  • C ++. auto키워드 트리거는 추론을 입력합니다. 그것 없이는 람다 또는 컨테이너의 항목에 대한 유형을 철자하는 것은 지옥이 될 것입니다.

  • 씨#. 을 사용하여 변수를 선언 var하면 제한된 형식의 형식 유추를 트리거 할 수 있습니다 . 유형 유추를 원하는 대부분의 경우를 관리합니다. 특정 장소에서는 유형을 완전히 생략 할 수 있습니다 (예 : 람다).

  • Haskell 및 ML 제품군의 모든 언어 여기에 사용 된 특정 유형의 유추 유형은 상당히 강력하지만 여전히 함수에 대한 유형 주석이 표시되는 두 가지 이유가 있습니다. 첫 번째는 문서이고 두 번째는 유형 유추가 실제로 예상 한 유형을 찾은 확인입니다. 불일치가있는 경우 일종의 버그가있을 수 있습니다.


13
C #에는 익명 형식, 즉 이름이없는 형식이 있지만 C #에는 명목 형식 시스템, 즉 이름을 기반으로하는 형식 시스템이 있습니다. 유형 유추가 없으면 이러한 유형을 사용할 수 없습니다!
Jörg W Mittag

10
내 의견으로는 일부 예가 약간 고안되었다. 42로 초기화한다고해서 자동으로 변수가이라는 의미는 아니며 int짝수를 포함한 모든 숫자 유형이 될 수 있습니다 char. 또한 Entry클래스 이름을 입력하고 IDE에서 필요한 가져 오기를 수행 할 수 있는 경우에 대한 전체 유형을 철자하려는 이유를 모르겠습니다 . 전체 이름을 입력해야하는 유일한 경우는 자신의 패키지에 동일한 이름을 가진 클래스가있을 때입니다. 그러나 어쨌든 나쁜 디자인처럼 보입니다.
Malcolm

10
@ Malcolm 예, 모든 예제가 고안되었습니다. 그들은 요점을 설명하는 역할을합니다. int예를 썼을 때 , 나는 타입 추론을 특징으로하는 대부분의 언어의 (제 생각에는 제정신의 행동)에 대해 생각하고있었습니다. 그들은 일반적으로 추론 int하거나 Integer또는 무엇이든이 해당 언어라고합니다. 형식 유추의 장점은 항상 선택 사항이라는 것입니다. 필요한 경우 다른 유형을 지정할 수 있습니다. Entry예제 와 관련하여 좋은 점은로 대체하겠습니다 Map.Entry<Integer, Map<Integer, SomeObject<SomeObject, T>>>. 자바는 타입 별칭조차 없다 :(
amon

4
@ m3th0dman 타입이 이해하는데 중요하다면, 여전히 명시 적으로 언급 할 수 있습니다. 형식 유추는 항상 선택 사항입니다. 그러나 여기서의 유형은 colKey명백하고 관련이 없습니다. 우리는 두 번째 주장으로 적합하다는 것만 걱정합니다 doSomethingWith. 반복문을 3 회 반복하는 함수로 루프를 추출하는 (key1, key2, value)경우 가장 일반적인 서명은 <K1, K2, V> Iterable<TableEntry<K1, K2, V>> flattenTable(Map<K1, Map<K2, V>> table)입니다. 이 함수 내에서 colKey( Integer, not K2) 의 실제 유형 은 절대적으로 관련이 없습니다.
amon

4
@ m3th0dman 이것은 " 대부분의 "코드가 이것 또는 저것에 관한 아주 광범위한 진술 입니다. 일화 통계. 이니셜 라이저에서 유형을 두 번 쓰는 데는 아무런 의미가 없습니다 View.OnClickListener listener = new View.OnClickListener(). 프로그래머가 "게으른"경우에도 유형을 알고있을 수 있습니다 var listener = new View.OnClickListener(가능한 경우). 이러한 종류의 중복성은 일반적입니다. 여기서는 추측 할 위험이 없습니다. 제거하는 것은 미래 독자에 대한 생각에서 비롯됩니다. 모든 언어 기능은주의해서 사용해야합니다.
Konrad Morawski

26

코드가 작성된 것보다 훨씬 자주 읽힌다는 것은 사실입니다. 그러나 읽기에도 시간이 걸리고 두 화면의 코드는 한 화면의 코드보다 탐색 및 읽기가 더 어렵 기 때문에 가장 유용한 정보 / 읽기 노력 비율을 우선 순위로 정해야합니다. 이것은 일반적인 UX 원칙입니다. 한 번에 너무 많은 정보가 압도되어 실제로 인터페이스의 효율성을 떨어 뜨립니다.

그리고 정확한 유형 중요 하지 않은 것은 종종 내 경험입니다 . 분명히 당신이 때때로 둥지 표현 : , , . 이들 각각에는 유형이 기록되지 않은 값으로 평가되는 하위 표현식이 포함됩니다. 그러나 그들은 완전히 명확합니다. 컨텍스트에서 파악하기가 충분히 쉽기 때문에 유형을 지정하지 않은 상태로 두는 것이 좋습니다. 데이터 흐름을 이해하지 못하면 데이터 흐름을 이해하고 귀중한 화면과 단기 메모리 공간을 확보해야합니다.x + y * zmonkey.eat(bananas.get(i))factory.makeCar().drive()

내일이없는 것처럼 표현식을 중첩하지 않는 한 가지 이유는 줄이 길어지고 값의 흐름이 명확하지 않기 때문입니다. 임시 변수를 도입하면 도움이되며 순서를 부과하고 부분 결과에 이름을 부여합니다. 그러나 이러한 측면에서 이점을 얻는 모든 것이 유형을 설명하는 것으로부터 이점을 얻는 것은 아닙니다.

user = db.get_poster(request.post['answer'])
name = db.get_display_name(user)

user엔터티 개체, 정수, 문자열 또는 다른 것이 중요합니까 ? 대부분의 경우 사용자가 HTTP 요청에서 왔으며 응답의 오른쪽 하단에 표시 할 이름을 가져 오는 데 사용된다는 것을 아는 것으로 충분하지 않습니다.

이 때 않는 문제, 저자는 유형을 작성하는 무료입니다. 이것은 책임감있게 사용해야하는 자유이지만 가독성 (변수 및 함수 이름, 형식, API 디자인, 공백)을 향상시킬 수있는 다른 모든 경우에도 마찬가지입니다. 실제로 Haskell과 ML의 규칙 ( 추가 노력없이 모든 것을 유추 할 수있는 경우)은 비 로컬 함수 함수의 유형과 적절한 경우마다 로컬 변수 및 함수를 작성하는 것입니다. 초보자 만 모든 유형을 유추 할 수 있습니다.


2
+1 이것은 정답입니다. 타입 추론이 좋은 아이디어 인 이유는 바로 핵심입니다.
Christian Hayter

user를 사용하여 수행 할 수있는 기능을 결정하므로 함수를 확장하려는 경우 정확한 유형 이 중요합니다 user. 예를 들어 보안 취약점으로 인해 위생 검사를 추가하거나 실제로 표시하는 것 외에도 사용자와 함께 무언가를 수행해야하는 것을 잊어 버린 경우에 중요합니다. 사실, 이러한 종류의 확장을위한 읽기는 코드를 읽는 것보다 덜 자주 수행되지만 우리 업무의 중요한 부분이기도합니다.
cmaster

@cmaster 그리고 당신은 항상 그 유형을 쉽게 찾을 수 있습니다 (대부분의 IDE는 당신에게 알릴 것이고 의도적으로 유형 오류를 유발하고 컴파일러가 실제 유형을 인쇄하게하는 최첨단 솔루션이 있습니다), 그것은 길을 벗어났습니다. 일반적인 경우에 당신을 귀찮게하지 않습니다.

4

타입 추론은 매우 중요하며 모든 현대 언어로 지원되어야한다고 생각합니다. 우리는 모두 IDE에서 개발하며 추론 된 유형을 알고 싶을 때 많은 도움을 줄 수 vi있습니다. 예를 들어 Java의 장황함과 의식 코드를 생각해보십시오.

  Map<String,HashMap<String,String>> map = getMap();

그러나 당신은 내 IDE가 나에게 도움이된다고 말할 수 있습니다. 유효한 포인트 일 수 있습니다. 그러나 C # 익명 형식과 같은 형식 유추의 도움 없이는 일부 기능이 없을 수 있습니다.

 var person = new {Name="John Smith", Age = 105};

Linq는 Select예를 들어 형식 유추의 도움이 없다면 지금처럼 좋지 않을 것입니다.

  var result = list.Select(c=> new {Name = c.Name.ToUpper(), Age = c.DOB - CurrentDate});

이 익명 유형은 변수에 깔끔하게 추론됩니다.

나는 Scala당신의 요점이 여기에 적용된다고 생각하기 때문에 반환 유형에 대한 유형 유추를 싫어합니다. 우리가 API를 더 유창하게 사용할 수 있도록 함수가 무엇을 반환하는지 분명해야합니다.


Map<String,HashMap<String,String>>? 물론 유형을 사용 하지 않는 경우 철자를 철자해도 큰 이점이 없습니다. Table<User, File, String>더 유익하고 작성하는데 이점이 있습니다.
MikeFHay

4

이것에 대한 대답은 정말 간단하다고 생각합니다. 중복 정보를 읽고 쓰는 것을 저장합니다. 특히 등호의 양쪽에 유형이있는 객체 지향 언어에서.

또한 정보가 중복되지 않을 때 사용하지 말아야 할 때를 알려줍니다.


3
기술적으로, 수동 서명을 생략 할 수 있으면 정보가 항상 중복됩니다. 그렇지 않으면 컴파일러가 정보를 유추 할 수 없습니다! 그러나 나는 당신이 의미하는 것을 얻습니다 : 단일 뷰에서 여러 지점에 서명을 복제 하면 실제로 두뇌에 중복됩니다. 잘 배치 된 유형은 오랫동안 검색해야 할 정보를 제공합니다. 많은 명백한 변형.
leftaroundabout

@leftaroundabout : 프로그래머가 읽을 때 중복됩니다.
jmoreno

3

코드를 본다고 가정 해 봅시다.

someBigLongGenericType variableName = someBigLongGenericType.someFactoryMethod();

경우 someBigLongGenericType의 반환 형식에서 할당 할 someFactoryMethod, 유형이 정확히 일치하지 않는 경우 가능성을 코드를 읽는 사람은 예고하는 것, 어떻게 쉽게 통지 불일치를 한 사람은 의도적인지 아닌지 알 수 있을까?

추론을 허용함으로써, 언어는 코드를 읽는 사람에게 변수의 유형이 명시 적으로 언급 될 때 그 이유를 찾아야한다고 제안 할 수 있습니다. 이를 통해 코드를 읽는 사람들이 자신의 노력에 더 집중할 수 있습니다. 반대로, 유형이 지정 될 때 대부분의 시간이 유추 된 시간과 정확히 동일하게 발생하면 코드를 읽는 사람이 미묘하게 다른 시간을 알아 채기가 쉽지 않을 수 있습니다. .


2

이미 많은 훌륭한 답변이 있음을 알았습니다. 그중 일부는 반복 될 것이지만 때로는 자신의 말로 물건을 넣고 싶어합니다. 나는 가장 친숙한 언어이기 때문에 C ++의 몇 가지 예를 언급 할 것입니다.

필요한 것은 결코 현명하지 않습니다. 다른 언어 기능을 실용화하려면 형식 유추가 필요합니다. C ++에서는 처리 할 수없는 유형을 가질 수 있습니다.

struct {
    double x, y;
} p0 = { 0.0, 0.0 };
// there is no name for the type of p0
auto p1 = p0;

C ++ 11은 말할 수없는 람다를 추가했습니다.

auto sq = [](int x) {
    return x * x;
};
// there is no name for the type of sq

형식 유추도 템플릿을 뒷받침합니다.

template <class x_t>
auto sq(x_t const& x)
{
    return x * x;
}
// x_t is not known until it is inferred from an expression
sq(2); // x_t is int
sq(2.0); // x_t is double

그러나 당신의 질문은 "프로그래머가 내가 코드를 읽을 때 변수의 타입을 추론하고 싶어하는 이유는 무엇입니까?"

유형 유추는 중복성을 제거합니다. 코드를 읽을 때 코드에 중복 정보를 저장하는 것이 더 빠르고 쉬울 있지만 중복성이 유용한 정보를 흐리게 할 수 있습니다 . 예를 들면 다음과 같습니다.

std::vector<int> v;
std::vector<int>::iterator i = v.begin();

C ++ 프로그래머가 i가 이터레이터임을 식별하기 위해 표준 라이브러리에 익숙하지 않으므로 i = v.begin()명시 적 유형 선언의 가치는 제한적입니다. 그 존재로 인해 더 중요한 세부 사항을 모호하게합니다 (예 : i벡터의 시작 부분을 가리킴). @amon의 정답은 중요한 세부 사항을 어둡게하는 자세한 표현의 예를 제공합니다. 반대로 형식 유추를 사용하면 중요한 세부 사항이 더 두드러집니다.

std::vector<int> v;
auto i = v.begin();

코드를 읽는 것이 중요하지만 충분하지 않지만 어느 시점에서 읽기를 중단하고 새 코드를 작성해야합니다. 코드의 중복성은 코드 수정 속도를 늦추고 어렵게 만듭니다. 예를 들어 다음 코드 조각이 있다고 가정 해보십시오.

std::vector<int> v;
std::vector<int>::iterator i = v.begin();

벡터의 값 유형을 변경하여 코드를 두 번 변경 해야하는 경우 :

std::vector<double> v;
std::vector<double>::iterator i = v.begin();

이 경우 코드를 두 곳에서 수정해야합니다. 원본 코드가 다음과 같은 형식 유추와 대조됩니다.

std::vector<int> v;
auto i = v.begin();

그리고 수정 된 코드 :

std::vector<double> v;
auto i = v.begin();

이제 한 줄의 코드 만 변경하면됩니다. 이것을 큰 프로그램으로 추정하면 형식 유추를 통해 편집기를 사용하는 것보다 훨씬 빠르게 변경 사항을 형식에 전파 할 수 있습니다.

코드 중복성으로 인해 버그가 발생할 수 있습니다. 코드가 동등하게 유지되는 두 가지 정보에 의존 할 때마다 실수의 가능성이 있습니다. 예를 들어,이 문장에서 두 유형 사이에 불일치가 있는데 이는 의도하지 않은 것입니다.

int pi = 3.14159;

중복성은 의도를 식별하기 어렵게 만듭니다. 경우에 따라 형식 유추가 명시 적 형식 지정보다 단순하기 때문에 읽고 이해하기가 더 쉽습니다. 코드 조각을 고려하십시오.

int y = sq(x);

sq(x)반환하는 경우 반환 유형 인지 또는 사용하는 문에 적합한 int지 여부 y는 확실하지 않습니다 . 더 이상 반환하지 않도록 다른 코드를 변경하면 해당 행에서만 유형을 업데이트 해야하는지 확실 하지 않습니다 . 동일한 코드와 대조하지만 형식 유추를 사용하십시오.intsq(x)ysq(x)inty

auto y = sq(x);

이것에서 의도는 명확하다 .에 y의해 반환 된 것과 같은 타입이어야한다 sq(x). 코드의 반환 형식을 변경하면 sq(x), 유형 y변경 사항이 자동으로 일치합니다.

C ++에서는 위의 예제가 형식 유추로 더 간단한 두 번째 이유가 있습니다. 유형 유추는 암시 적 유형 변환을 도입 할 수 없습니다. 의 반환 유형 sq(x)이 아닌 int경우 컴파일러는에 암시 적 변환을 자동으로 삽입합니다 int. 의 반환 유형 이을 sq(x)정의하는 유형 복합 유형 인 operator int()경우이 숨겨진 함수 호출은 임의로 복잡 할 수 있습니다.


C ++에서 처리 할 수없는 유형에 대한 좋은 지적입니다. 그러나 언어를 수정하는 이유보다 유형 유추를 추가하는 이유가 아니라고 생각합니다. 첫 번째 경우, 프로그래머는 형식 유추를 사용하지 않기 위해 이름을 지정해야하므로 강력한 예는 아닙니다. 두 번째 예제는 C ++에서 람다 형식을 명시 적으로 금지하고 사용하여 형식 정의를 사용하더라도 typeof언어에서 쓸모가 없게 되기 때문에 강력 합니다. 그리고 그것은 내 의견으로는 고쳐야 할 언어 자체의 결함입니다.
cmaster
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.