Go Generic ㄷㄷㄷㅈ

드디어 Go에도 제네릭이!

1234

두둥등장!

드디어 Go에도 제네릭이 등장했다.

구글의 Go팀은 계속해서 제네릭의 추가 가능성을 언급했고, 드디어 어제 Go의 제네릭이 모습을 드러냈다.

사실상 업데이트를 해도 0.1버전과 현재버전의 차이가 크게 없는 Golang의 특징상 앞으로 제네릭의 등장은 사실상 BC,AC처럼 분명한 경계점이 될 것은 틀림없어 보인다.

함께 Go의 공식 사이트를 보면서 어떻게 사용하는지, 무엇이 바뀌었는지 살펴보자.

근데 정식 오픈은 아니고 아직 컨셉만 공개

호들갑 떨었지만 정식 출시는 아니다.

이렇게 만들려고 한다~ 정도의 느낌이니까 어떻게 개발할 생각인지 간략하게 살펴보려고 한다.

그전에 프로토타입 모델의 제네릭을 공개한 이유는 여러가지로 추측된다.

우선 첫 번째로는 매년 Go에서 실시하는 “Go에 이런게 추가되었으면 좋겠어요”에서 제네릭은 항상 1위를 차지했었기 때문에 사람들의 관심(?)을 끌려는 목적이 분명하다.

두 번째는 관심을 끌어서 사람들의 반응과 피드백을 보고 싶은 모양이다.

Feedback

The best way to provide feedback for the language changes will be on the mailing list golang-nuts@googlegroups.com. Mailing lists are imperfect, but they seem like our best option for initial discussion. When writing about the design draft, please put [generics] at the start of the Subject line and to start different threads for different specific topics.

If you find bugs in the generics type checker or the translation tool, they should be filed in the standard Go issue tracker at https://golang.org/issue. Please start the issue title with cmd/go2go:. Note that the issue tracker is not the best place to discuss changes to the language, because it does not provide threading and it is not well suited to lengthy conversations.

We look forward to your feedback.

공식 문서에서는 대놓고 피드백좀 주셈이라고 요청하고 있는 것을 볼 수 있다.

예제 문서

예제 문서가 있기는 한데 분량이 너무 방대해서 간단하게 예제 몇 개만 슥 훑어봤다.

대충 음~ 얘들이 이렇게 만드려고 하는구나 정도만 볼 생각이었기 때문이다.

// Print prints the elements of a slice.
// It should be possible to call this with any slice value.
func Print(s []T) { // Just an example, not the suggested syntax.
	for _, v := range s {
		fmt.Println(v)
	}
}

우리가 JVM 계통에서 익히 봐왔던 제네릭의 형태와 크게 다르지 않아 보인다.

func Print(type T)(s []T) {
	// same as above
}

또한 위와 같이 형식 인자를 넘겨주게 설정도 가능하다.

	// Call Print with a []int.
	// Print has a type parameter T, and we want to pass a []int,
	// so we pass a type argument of int by writing Print(int).
	// The function Print(int) expects a []int as an argument.

	Print(int)([]int{1, 2, 3})

	// This will print:
	// 1
	// 2
  // 3

타입 인자를 넘겨주고 있고, int 타입의 슬라이스도 넘겨주는 것을 확인할 수 있다.

개인적으로 왜 굳이..? 이런걸 추가했나 싶긴 한데 뭐 이게 더 직관적이라고 생각했는 모양이다.

주의점

// This function is INVALID.
func Stringify(type T)(s []T) (ret []string) {
	for _, v := range s {
		ret = append(ret, v.String()) // INVALID
	}
	return ret
}

조심해야할 점은 위와 같이 s의 타입이 정해지지 않았기 때문에 v 역시 String() 메소드를 호출할 수 없어서 에러가 발생한다.

그러면서 글에서는 C++과의 비교를 하고 있는데, C++의 경우 String() 메소드를 호출해도 괜찮다.

만약 String 메서드가 없다면 컴파일 타임에 에러를 발생시킨다.

이러한 오류의 발견은 함수가 여러 계층으로 있으면 더 늦어질 수도 있다.

고에서는 이러한 접근을 하지 않고, 딱 봤을때 String() 메소드 없으면 바로 에러를 내버린다. (단호!)

특히 Go가 대용량 애플리케이션 개발에 초점을 맞추고 설계된 언어인 만큼 이러한 제약 조건을 명확하게 가져가기로 했다고 한다.

그래서 이러한 제약조건을 인터페이스로 풀었는데 이러한 인터페이스를 Go에서는 constraints라고 부르는 것으로 보인다.

// Stringer is a type constraint that requires the type argument to have
// a String method and permits the generic function to call String.
// The String method should return a string representation of the value.
type Stringer interface {
	String() string
}

Stringer라는 인터페이스를 정의하고 String 메소드를 넣어둔다.

// Stringify calls the String method on each element of s,
// and returns the results.
func Stringify(type T Stringer)(s []T) (ret []string) {
	for _, v := range s {
		ret = append(ret, v.String())
	}
	return ret
}

그리고 아까 타입 인자에 Stringer constraints를 주면 위 문제를 해결할 수 있다.

어째 좀 더 복잡해보이는건 기분탓일까?

뭐 어쨌든 이런 식으로 제네릭 타입의 메소드를 컴파일 타임에 알아차릴 수 있게 인터페이스를 이용해서 지원해주는 기능도 있다고 한다.

재밌는 기능이 더 많아 보이기는 한데 시간 관계상 여기까지만 하도록 하려고 한다.

더 자세한 내용은 아래 참조에 링크를 걸어두었으니 자세히 살펴보길 바란다.

그래서 정식 출시는 언제?

공식 문서에서는 아마도 내년 여름에는 되야 출시할 예정이라고 한다.

 the earliest that generics could be added to Go would be the Go 1.17 release, scheduled for August 2021

Reference

https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md

https://blog.golang.org/generics-next-step



© 2022. by minkuk

Powered by minkuk