-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
타입으로 견고하게 다형성으로 유연하게 3주차 - 하업서 #469
The head ref may contain hidden characters: "463-\uD0C0\uC785\uC73C\uB85C-\uACAC\uACE0\uD558\uAC8C-\uB2E4\uD615\uC131\uC73C\uB85C-\uC720\uC5F0\uD558\uAC8C-3\uC7A5-\uCD1D-73\uD398\uC774\uC9C0-2025-02-07-Harry"
타입으로 견고하게 다형성으로 유연하게 3주차 - 하업서 #469
Conversation
우측에 있는 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
``` | ||
|
||
### 논제 ### | ||
1. 힌들리-밀너 타입 추론은 하스캘과 오캐멀 언어 예제만 책에 소개되었는데. 하당 타입 추론 방식을 다른 언어에서는 잘 활용하지 않는 것으로 보인다. 그렇다면 그 이유는 무엇일까? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그렇다기 보다는 책의 예제가 하스켈, 오캐멀일 뿐이고
제네릭을 사용하는 모든 프로그래밍 언어라면 힌들리-밀러 타입 추론이 가능합니다.
이미 업서님이 swift로 올려준 제네릭 코드들의 예제들이 모드 타입 추론이 적용된 코드들을 올려 주셨습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
제네릭 함수를 쓰려면 우선 아래와 같이 정의를 해야 합니다.
func add<T: Numeric>(v1: T, v2: T) -> T {
return v1 + v2
}
그러면 원래 제네릭 함수를 Int 타입으로 사용한다면 아래와 같이 써야 하는데,
하지만 실제 컴파일 해보면 명시적으로 Int를 지정할 수 없다고 에러가 나옵니다.
hello<Int>(v1: 1, v2: 2)
하지만 타입 추론이 적용되므로 아래와 같이 사용하는 거죠
hello(v1: 1, v2: 2)
그리고 책에 힌들리-밀너 타입 추론의 각주에 다음과 같은 설명이 있습니다.
정확히 말하면, 제네릭 함수를 정의할 때도 타입 추론을 하는 방식 중 하나가 힌들리-밀너 타입 추론이다. ... 그럼에도 이 책에서는 제네릭 함수를 정의할 때 타입 추론을 하는 것을 그냥 힌들리-밀너 타입 추론이라 부른다. 그 이유는 현실 세계 언어에 적용되어 널리 사용되는 것은 실질적으로 힌들리-밀너 타입 추론뿐이며, 어차피 나머지 방식도 대개 힌들리-밀너 타입 추론의 확장이라 볼 수 있기 때문이다.
따라서 저는 제네릭 함수로 타입 추론이 되는 것 역시 힌들리-밀너 추론이라고 했던 겁니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
얘기를 들어보고 자료를 찾아보니 힌들리-밀너 타입 추론이 포괄하는 범위가 조금 넓은 것 같습니다. 책에서 설명하는 부분은 아래와 같은데 조금은 이야기하는 방향이 다른 느낌이 듭니다.
저는 아래와 같은 상황으로 이해를 하고 있는 상황인데요.
하스켈 (완전한 타입 추론 가능)
함수 설계, 타입 추론은 제네릭 명시하지 않아도 제네릭일 경우 제네릭으로 타입 추론
함수 호출, 타입 추론 가능
Swift (일부만 타입 추론 가능)
함수 설계, 타입 추론은 제네릭을 명시해야만 가능
함수 호출, 타입 추론 가능
아래는 직접 코드를 작성해본 것인데,
하스켈은 제네릭을 명시 안해도 함수 설계가 가능한데,
Swift는 불가능합니다.
다른 언어는 되는데 Swift만 이런 것일까요?



There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
제가 읽은 미디엄 글에 따르면,
함수형 언어는 기본적으로 타입 추론 알고리즘을 통해 정적 타입을 구현하는데, 일반적으로 힌들리 밀너 타입 시스템을 사용한다고 나와있는데요
각 언어 마다 설계 철학이 다르기 때문에, 함수형 언어가 아니라면, 굳이 위 개념을 도입할 이유가 없기 때문이 아닐까 라고 생각�했습니다 꼭 힌들리 밀너 타입 시스템(책과 다르게 엄밀한 의미라고 할 때,)을 쓰지 않더라도 제네릭을 해당 언어에서 사용하게 할 수 있는 방법은 다양하기 때문이고, 힌들리 밀너 타입 시스템 처럼 극단적으로 타입추론을 제공해야한 마땅한 이유가 없기 때문이기도 할 것 같습니다

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AI의 답변을 첨부합니다..
힌들리–밀너(Hindley–Milner, HM) 타입 추론 알고리즘은 순수 함수형 언어(예: ML, Haskell)에서 매우 효과적으로 동작하는 강력한 타입 추론 시스템입니다. 그러나 객체지향 언어에서는 HM 타입 추론을 완전히 채택하기 어려운데, 그 이유는 주로 다음과 같습니다.
서브타이핑(Subtyping)과 다형성의 복잡성
함수형 언어에서는 주로 **전역적인 파라메트릭 다형성(parametric polymorphism)**을 사용하며, 이 경우 HM 알고리즘이 “주요 타입(principal type)”을 보장하면서 완전한 타입 추론을 할 수 있습니다. 반면, 객체지향 언어는 클래스 계층 구조, 상속, 인터페이스, 오버라이딩 등 서브타이핑과 관련된 복잡한 개념을 포함합니다.
서브타이핑은 타입 간의 포함 관계를 도입하는데, 이는 HM 알고리즘이 전제로 하는 단순한 파라메트릭 다형성과는 상충됩니다.
서브타이핑이 포함되면 타입 추론이 모호해지거나 결정 불가능한 경우가 발생할 수 있습니다.
가변 상태와 부작용
함수형 언어는 순수함수를 지향하여 부작용이 없거나 최소화된 환경에서 타입 추론을 수행할 수 있습니다. 그러나 객체지향 언어는 객체의 상태 변경(가변 상태)과 부작용을 허용하는 경우가 많습니다.
가변 상태는 타입 추론을 복잡하게 만들어, HM 알고리즘이 적용되기 어려운 상황을 만듭니다.
동적 바인딩과 런타임 특성
객체지향 언어에서는 런타임에 결정되는 동적 바인딩이나 다형적 호출 등의 특성이 있습니다. 이는 컴파일 타임에 완전한 타입 정보를 추론하기 어렵게 만듭니다.
언어 설계 목표와 타협
객체지향 언어들은 보통 명시적인 타입 표기(explicit typing)를 통해 개발자가 코드의 의미를 명확하게 표현하도록 설계되었습니다. 이는 타입 추론의 범위를 제한하는 동시에, 코드의 가독성과 유지보수성을 향상시키려는 의도도 있습니다.
예를 들어, Java나 C#에서는 제한적인 형태의 타입 추론(예: Java의 var 또는 C#의 var)만 지원하며, 이는 HM 알고리즘처럼 전역적이고 완전한 추론은 아닙니다.
요약하면,
순수 함수형 언어는 부작용이 적고, 파라메트릭 다형성이 주된 특징이기 때문에 HM 타입 추론이 잘 맞고 효과적으로 적용됩니다.
객체지향 언어는 서브타이핑, 가변 상태, 동적 바인딩 등 복잡한 개념들이 포함되어 있어 HM 알고리즘이 요구하는 제약(예, 주된 다형성과 순수성)을 만족시키지 못하며, 이로 인해 완전한 HM 타입 추론을 구현하기 어렵습니다.
이러한 이유들로 인해 함수형 언어에서는 힌들리–밀너 타입 추론이 널리 사용되는 반면, 객체지향 언어에서는 그 대신 명시적인 타입 표기나 부분적인 타입 추론 기법(또는 전혀 다른 타입 시스템)을 채택하게 됩니다.
3주차 매개변수에 의한 다형성
제네릭 타입은 JSON Decoder에서 지정한 타입으로 값을 가져올 때 많이 사용했고, 한 때는 Any와 Generic의 차이를 잘 몰랐는데 이번 기회에 조금 더 정확히 알 수 있어 좋았습니다.
이 밖에 프로토콜과 제네릭을 같이 사용하면 강력한 다형성 기능을 구현할 수 있다고 주워들었는데, 책에는 소개되지 않았지만 무엇이든 타입과 무엇인가 타입을 Swift 언어로 구현하면서는 프로토콜을 활용하게 되었는데 (비록 GPT의 도움을 받았지만) 직접 코딩으로 작성해보니 아직은 저런 문법을 잘 활용하지는 않지만... 책에서 얘기하는 내용이 어느정도 이해가 되었습니다. 조금 더 유연하게 타입을 지정하면서 함수를 동시에 교체하면서 다소 제한적이지만 기능도 바꿀 수 있어보입니다.
무엇인가 타입은 타입을 감추는 것으로 활용하는 것 같은데, 같은 맥락인지는 모르겠지만 swift에는 some라는 opaque 타입을 통해서 감출 수 있는 것 같습니다.