형식 유추에 대한 장단점은 무엇입니까?


29

모든 새로운 프로그래밍 언어 또는 최소한 대중화 된 언어는 유형 유추를 사용하는 것으로 보입니다. Javascript조차도 다양한 구현 (Acscript, typescript 등)을 통해 유형과 유형 유추를 얻었습니다. 그것은 나에게 멋져 보이지만 어떤 절충점이 있는지 또는 왜 Java 또는 오래된 좋은 언어에 유형 유추가 없다고 말합니까?

  • 유형을 지정하지 않고 (유형이나 : = 구문없이 var를 사용하여) Go 에서 변수를 선언하면 변수 유형이 오른쪽의 값에서 유추됩니다.
  • D를 사용하면 동적 언어처럼 유형을 중복 지정하지 않고도 큰 코드 조각을 작성할 수 있습니다. 반면에 정적 추론은 유형과 다른 코드 속성을 추론하여 정적 및 동적 세계를 최대한 활용합니다.
  • Rust 의 타입 추론 엔진 은 꽤 똑똑합니다. 초기화하는 동안 r 값의 유형을 보는 것 이상을 수행합니다. 또한 변수가 나중에 유형을 유추하는 데 어떻게 사용되는지 살펴 봅니다.
  • Swift 는 형식 유추를 사용하여 적절한 형식을 해결합니다. 형식 유추를 통해 컴파일러는 코드를 컴파일 할 때 제공 한 값을 검사하여 특정 식의 형식을 자동으로 추론 할 수 있습니다.

3
C #에서 일반 지침은 때때로 가독성을 손상시킬 수 있으므로 항상 사용 하지는 않는다고 말합니다 var.
Mephy

5
"... 또는 왜 Java 나 오래된 좋은 언어에 유형 유추가 없다고 가정 해 봅시다"그 이유는 아마도 역사적 일 것입니다. ML은 Wikipedia에 따르면 C 후 1 년이 지나서 나타 났으며, 유형 유추가있었습니다. 자바는 C ++ 개발자들에게 어필하려고했다 . C ++은 C의 확장으로 시작했으며 C의 주요 관심사는 어셈블리에 대한 휴대용 래퍼이며 컴파일하기 쉽다는 것입니다. 가치가있는 것은 무엇이든, 하위 유형 지정은 일반적인 경우에도 유형 유추를 결정할 수 없다는 것을 읽었습니다.
Doval

3
@Doval Scala는 하위 유형 상속을 지원하는 언어에 대해 유추 유형에서 꽤 잘 작동하는 것 같습니다. ML 계열 언어만큼 좋지는 않지만 언어 디자인에 따라 요청하는 것만 큼 좋습니다.
KChaloux 2019

12
형식 추론 (C # var및 C ++와 같은 단방향 auto)과 형식 추론 (Haskell과 같은 양방향 )을 구분하는 것이 좋습니다 let. 전자의 경우, 이름의 형식은 자사의 initialiser로부터 추론 할 수있다 가 사용하는 이름의 형식을 따라야합니다. 후자의 경우, 이름의 유형은 그 사용에서도 유추 될 수 있습니다. 이는 []요소 유형에 관계없이 빈 시퀀스에 대해 간단히 쓸 수 있다는 점에서 유용합니다.newEmptyMVar 참조 자에 관계없이 새로운 널 변경 가능 참조에 유형.
Jon Purdy

형식 유추는 특히 효율적으로 구현하기가 매우 복잡 할 수 있습니다. 사람들은 컴파일 복잡성이 더 복잡한 표현으로 인해 급격히 증가하는 것을 원하지 않습니다. 재미있는 읽을 거리 : cocoawithlove.com/blog/2016/07/12/type-checker-issues.html
Alexander-Monica Monica

답변:


43

Haskell의 타입 시스템은 완전히 추론 할 수 없지만 (다형성 재귀, 특정 언어 확장 및 끔찍한 독점 제한을 제외하고) 프로그래머는 필요하지 않더라도 여전히 소스 코드에 타입 주석을 제공합니다. 왜?

  1. 타입 주석은 문서 ​​역할을합니다 . 이것은 Haskell과 같은 표현형에서 특히 그렇습니다. 함수의 이름과 유형이 주어지면 일반적으로 함수가 수행하는 기능, 특히 함수가 파라 메트릭 다형성 인 경우에 대해 상당히 잘 추측 할 수 있습니다.
  2. 타입 주석은 개발을 이끌 수 있습니다 . 함수 본문을 작성하기 전에 형식 서명을 작성하면 테스트 중심 개발과 같은 느낌이 듭니다. 내 경험상, Haskell 함수를 컴파일하면 일반적으로 처음 작동합니다. (물론 이것은 자동화 된 테스트가 필요하지 않습니다!)
  3. 명시 적 유형은 가정을 확인하는 데 도움이 될 수 있습니다 . 이미 작동하는 일부 코드를 이해하려고 할 때 올바른 유형 주석이라고 생각하는 코드를 자주 사용합니다. 코드가 여전히 컴파일되면 이해했습니다. 그렇지 않으면 오류 메시지를 읽습니다.
  4. 형식 서명을 사용하면 다형성 함수를 특수화 할 수 있습니다 . 특정 함수가 다형성이 아닌 경우 API가 더 표현력이 있거나 유용한 경우가 종종 있습니다. 함수 가 추론보다 일반적인 유형 을 제공하면 컴파일러는 불평하지 않습니다 . 고전적인 예는map :: (a -> b) -> [a] -> [b] 입니다. 보다 일반적인 형식 ( fmap :: Functor f => (a -> b) -> f a -> f b) Functor은 목록뿐만 아니라 모든에 적용됩니다 . 그러나 map초보자에게는 이해하기 쉬울 것이라고 생각되어 큰 형과 함께 살고 있습니다.

전체적으로 정적 형식이지만 유추 할 수없는 시스템의 단점은 일반적으로 정적 타이핑의 단점과이 사이트 및 기타 사이트에 대한 잘 알려진 토론의 단점과 거의 동일합니다. 화염 전쟁의 페이지 수). 물론, 상기 단점 중 일부는 유추 할 수있는 시스템에서 적은 양의 타입 주석에 의해 개선된다. 또한 형식 유추에는 고유 한 장점 이 있습니다. 형식 유추 없이는 홀 중심 개발 이 불가능합니다.

Java *는 너무 많은 유형 주석이 필요한 언어가 성가신다는 것을 증명하지만, 너무 적 으면 위에서 설명한 장점을 잃어 버립니다. 옵트 아웃 유형 유추가있는 언어는 두 극단 사이에 균형이 맞습니다.

*이 위대한 희생양 인 Java조차도 일정량의 로컬 유형 유추를 수행합니다 . 와 같은 문장 Map<String, Integer> = new HashMap<>();에서 생성자의 제네릭 형식을 지정할 필요는 없습니다. 반면에 ML 스타일 언어는 일반적으로 전 세계적으로 유추 할 수 있습니다.


2
그러나 당신은 초보자가 아닙니다;) 당신은 정말로 "가져 오기"전에 타입 클래스와 종류를 이해해야 Functor합니다. map양념이없는 Haskellers를 나열하고 더 잘 알고 있습니다.
벤자민 호지 슨

1
C # var을 형식 유추의 예 라고 생각 하십니까?
Benjamin Hodgson

1
예, C # var이 적절한 예입니다.
Doval

1
사이트에서 이미이 토론에 너무 긴 시간을 표시 했으므로 마지막 답장이됩니다. 전자에서 컴파일러는 형식을 결정해야합니다 x. 후자에는 결정할 유형이 없으며 모든 유형이 알려져 있으며 표현식이 의미가 있는지 확인하기 만하면됩니다. 사소한 예제를 지나갈 때 차이가 더 중요 해지고 x여러 곳에서 사용됩니다. 그런 다음 컴파일러는 다음 x을 결정하는 데 사용되는 위치를 교차 확인해야합니다. 1) x코드에서 유형을 검사하도록 유형 을 할당 할 수 있습니까? 2) 그렇다면 가장 일반적인 유형은 무엇입니까?
Doval

1
있습니다 new HashMap<>();구문은 자바 7에 첨가하고, 자바 (8)의 람다가 "진짜"형식 유추 꽤 많이 허용합니다.
Michael Borgwardt

8

C #에서 형식 유추는 컴파일 타임에 발생하므로 런타임 비용은 0입니다.

스타일 문제로 var유형을 수동으로 지정하는 것이 불편하거나 불필요한 상황에 사용됩니다. Linq는 그러한 상황 중 하나입니다. 다른 것은 :

var s = new SomeReallyLongTypeNameWith<Several, Type, Parameters>(andFormal, parameters);

그것 없이는 단순히 말하지 않고 정말 긴 유형 이름과 유형 매개 변수를 반복합니다 var .

명시적일 때 코드 명확성을 향상시킬 때 실제 유형 이름을 사용하십시오.

생성 시간에 값이 설정되거나 실제로 인텔리전스가 제대로 작동하기를 원하는 멤버 변수 선언과 같이 타입 유추를 사용할 수없는 상황이 있습니다 (해커 랭크의 IDE는 타입을 명시 적으로 선언하지 않으면 변수의 멤버를 인텔리전스하지 않습니다) .


16
"C #에서 형식 유추는 컴파일 타임에 발생하므로 런타임 비용은 0입니다." 형식 유추는 정의에 따라 컴파일 타임에 발생하므로 모든 언어에서 발생합니다.
Doval

훨씬 좋습니다.
Robert Harvey

1
@Doval JIT 컴파일 중에 타입 추론을 수행 할 수 있으며, 이는 런타임 비용을 분명히 가지고 있습니다.
Jules

6
@Jules 프로그램을 실행하는 한 이미 유형 검사를 마쳤습니다. 추론 할 것이 없다. 적절한 용어가 무엇인지 잘 모르겠지만 일반적으로 유형 유추라고하는 것은 아닙니다.
Doval

7

좋은 질문!

  1. 유형에 명시 적으로 주석이 달지 않기 때문에 때때로 코드를 읽기가 더 어려워서 더 많은 버그가 발생할 수 있습니다. 제대로 코드 청소기 물론 그것을 만드는 데 사용 보다 읽기가. 당신이 비관적있어 대부분의 프로그래머가 나쁜 (또는 대부분의 프로그래머가 작업이라고 생각하면 된다 나쁜),이 순 손실이 될 것입니다.
  2. 형식 유추 알고리즘은 비교적 간단하지만 무료 는 아닙니다. . 이런 종류의 것은 컴파일 시간을 약간 증가시킵니다.
  3. 유형에 명시 적으로 주석이 달지 않기 때문에 IDE는 사용자가 수행하려는 작업을 추측 할 수 없으므로 선언 프로세스 중에 자동 완성 및 유사한 도우미에 해를 끼칩니다.
  4. 함수 오버로드와 결합하여 유형 유추 알고리즘이 경로를 결정하지 못하는 상황에 처할 수있어 캐스팅 스타일 주석이 더 어려워집니다. (이것은 예를 들어 C #의 익명 함수 구문에서 상당히 발생합니다).

그리고 명시 적 유형 주석없이 이상한 것을 할 수없는 더 난해한 언어가 있습니다. 지금까지 내가 아는 것은 지나가는 것을 제외하고 언급 할만 큼 공통적 / 인기있는 / 유망한 것이 없습니다.


1
자동 완성 기능은 형식 유추에 대한 똥을주지 않습니다. 유형이 어떻게 결정되었는지는 중요하지 않으며 유형 만 가지고 있습니다. 그리고 C #의 람다 문제는 추론과 관련이 없습니다. 람다는 다형성이기 때문에 형식 시스템이 그것에 대처할 수 없기 때문에 추론과 관련이 없습니다.
DeadMG

2
@deadmg-변수가 선언되면 아무런 문제가 없지만 변수 선언을 입력하는 동안 선언 된 유형이 없으므로 오른쪽 옵션을 선언 된 유형으로 좁힐 수 없습니다.
Telastyn

@ deadmg-람다에 관해서는, 나는 당신이 의미하는 바에 대해 명확하지 않지만, 일이 어떻게 작동하는지에 대한 나의 이해에 근거하여 완전히 정확하지는 않다고 확신합니다. var foo = x => x;언어는 x여기서 추론 할 필요 가없고 계속 진행할 것이 없기 때문에 단순한 것조차 실패합니다 . 람다는 람다가 생성되고 명시 적으로 유형이 지정된 함수 Func<int, int> foo = x => x로 빌드되는 것처럼 명시 적 유형의 델리게이트 가 CIL Func<int, int> foo = new Func<int, int>(x=>x);에 빌드 될 때 빌드됩니다. 나에게, 타입을 유추하는 것은 타입 유추의 x일부이고 소포입니다.
Telastyn

3
@Telastyn 언어에 아무런 변화가 없다는 것은 사실이 아닙니다. 표현식을 입력하는 데 필요한 모든 정보 x => x는 람다 자체에 포함되어 있습니다. 주변 범위의 변수는 아닙니다. 모든 제정신 함수형 언어는 제대로 "모든 종류의 유형이 있음을 추론 할 것이다 a, a -> a". DeadMG가 말하려는 것은 C #의 유형 시스템에 주요 유형 속성 이 없기 때문에 항상 모든 표현식에 대해 가장 일반적인 유형을 파악할 수 있습니다. 파괴하기 매우 쉬운 재산입니다.
Doval

@Doval-enh ... C #에는 일반 함수 객체와 같은 것이 없으며 같은 문제가 발생하더라도 둘 다 말하는 것과 미묘하게 다르다고 생각합니다.
Telastyn

4

그것은 나에게 멋져 보이지만 어떤 절충점이 있는지 또는 왜 Java 또는 오래된 좋은 언어에 유형 유추가 없다고 말합니까?

Java는 여기서 규칙이 아니라 예외입니다. 심지어 C ++ ( "좋은 구식 언어":라고 생각합니다)조차도 C ++ auto11 표준 이후로 키워드의 형식 유추를 지원합니다 . 변수 선언뿐만 아니라 함수 반환 유형으로도 작동하며 복잡한 템플릿 함수에 특히 유용합니다.

암시 적 타이핑 및 형식 유추에는 많은 유스 케이스가 있으며 실제로 수행하지 않아야하는 유스 케이스도 있습니다. 이것은 때때로 맛의 문제이며 토론의 대상이기도합니다.

그러나 의심 할 여지없이 좋은 유스 케이스가 있다는 것은 그 자체로 모든 언어가이를 구현하는 합법적 인 이유입니다. 또한 기능을 구현하기가 어렵고 런타임 패널티가 없으며 컴파일 시간에 큰 영향을 미치지 않습니다.

개발자가 형식 유추를 사용할 수있는 기회를 제공하는 데 실제로 단점이 없습니다.

어떤 답변자는 때로는 명시적인 타이핑이 얼마나 좋은지 추론하고 확실히 옳았습니다. 그러나 암시 적 타이핑을 지원하지 않으면 언어가 항상 명시적인 타이핑을 시행한다는 의미입니다.

따라서 실제 단점 은 언어 암시 적 유형 지정을 지원 하지 않는 경우입니다. 이는 언어 를 사용하는 개발자가 합법적 인 이유가 없기 때문에 사실이 아닙니다.


1

Hindley-Milner 형식 추론 시스템과 Go 스타일 형식 추론의 주요 차이점은 정보 흐름의 방향입니다. HM에서 유형 정보는 통합을 통해 앞뒤로 흐릅니다. Go에서 유형 정보는 전달 만 전달합니다. 전달 대체 만 계산합니다.

HM 형식 추론은 다형성 형식의 기능 언어와 잘 작동하는 화려한 혁신이지만 Go의 저자는 아마도 너무 많은 일을 시도한다고 주장 할 것입니다.

  1. 정보가 앞뒤로 흐른다는 사실은 HM 유형 추론이 매우 비현실적인 일임을 의미합니다. 형식 주석이 없으면 프로그램의 모든 코드 줄이 단일 코드 줄 입력에 기여할 수 있습니다. 순방향 대체 만있는 경우, 유형 오류의 소스는 오류 앞에있는 코드에 있어야합니다. 이후의 코드가 아닙니다.

  2. HM 타입 추론을 사용하면 제약 조건: 유형을 사용할 때 가능한 유형을 제한합니다. 하루가 끝나면 완전히 제한되지 않은 유형 변수가있을 수 있습니다. HM 유형 추론에 대한 통찰은 이러한 유형이 실제로 중요하지 않으므로 다형성 변수로 만들어 졌다는 것입니다. 그러나 이러한 추가 다형성은 여러 가지 이유로 바람직하지 않을 수 있습니다. 첫째, 일부 사람들이 지적 했듯이이 여분의 다형성은 바람직하지 않을 수 있습니다 .HM은 가짜를 결정하고 일부 가짜 코드의 다형성 유형은 나중에 이상한 오류를 유발합니다. 둘째, 유형이 다형성으로 남아 있으면 런타임 동작에 영향을 줄 수 있습니다. 예를 들어, 지나치게 다형성 중간 결과가 'show'인 이유입니다. 읽기 '는 Haskell에서 모호한 것으로 간주됩니다. 다른 예로서,


2
불행히도 이것은 다른 질문에 대한 좋은 대답처럼 보입니다. OP는 "Go-style"대 HM이 아닌 "go-style 형식 유추"와 형식 유추가 전혀없는 언어에 대해 묻고 있습니다.
Ixrec

-2

가독성이 떨어집니다.

비교

var stuff = someComplexFunction()

vs

List<BusinessObject> stuff = someComplexFunction()

github와 같은 IDE 외부에서 읽을 때 특히 문제가됩니다.


4
이것은 이전의 6 가지 답변에서 제시되고 설명 된 점들에 비해 실질적인 것을 제공하지 않는 것 같습니다
gnat

"복잡한 읽을 수없는"대신 올바른 이름을 사용하면 var가독성을 손상시키지 않고 사용할 수 있습니다. var objects = GetBusinessObjects();
Fabio

좋아, 그러면 유형 시스템을 변수 이름으로 유출합니다. 헝가리 표기법과 같습니다. 리팩토링 후에는 코드와 일치하지 않는 이름으로 끝날 가능성이 높습니다. 일반적으로 기능적 스타일은 클래스가 제공하는 자연스러운 이름 지정 이점을 잃게합니다. 따라서 square.area () 대신 더 많은 squareArea ()로 끝납니다.
미구엘
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.