안녕하세요?
Go 1.22가 출시된 지 꽤 많은 시간이 지났는데요.
이제 제가 1.22 버전을 정리하는 시리즈의 마지막을 장식할 때가 왔습니다.
이전에 작성한 reflect.TypeFor
와 slices.Concat
함수에 대한 글을 먼저 읽으시는게 좋을 듯 합니다.
오늘 소개할 마지막 함수는 바로 cmp.Or
입니다.
실제로 이 함수의 개발자는 Go Time 팟캐스트에서 이 함수를 "1.22의 숨겨진 보석"이라고 소개한 바 있습니다.
간단한 기능이지만 다양한 활용 가능성을 가지고 있으며, 그 탄생 배경에는 놀랍도록 긴 이야기가 숨겨져 있습니다.
cmp.Or
: 제네릭 기반 조건부 값 선택의 새로운 접근 방식
cmp.Or
함수는 가변 개수의 인자를 받아, 첫 번째 non-zero 값을 반환하는 제네릭 함수입니다.
모든 인자가 zero 값인 경우에는 zero 값을 반환합니다.
이 함수의 코드는 다음과 같습니다.
// Or returns the first of its arguments that is not equal to the zero value.
// If no argument is non-zero, it returns the zero value.
func Or[T comparable](vals ...T) T {
var zero T
for _, val := range vals {
if val != zero {
return val
}
}
return zero
}
Go에 대한 저의 기여가 대부분 그렇듯, cmp.Or
함수 역시 매우 간결하고 직관적인 코드로 구현되었습니다.
단순히 입력받은 인자들을 순회하면서 zero 값이 아닌 첫 번째 값을 반환하는 로직입니다.
cmp.Or
의 활용: 다양한 상황에서의 유연한 값 선택
cmp.Or
함수는 주로 문자열 처리에서 빈 문자열("")을 대체할 기본값을 설정하는 데 사용됩니다.
예를 들어, 환경 변수를 가져오는 코드에서 환경 변수가 설정되지 않은 경우 기본값을 사용하고 싶을 때 cmp.Or(os.Getenv("SOME_VARIABLE"), "default")
와 같이 사용할 수 있습니다.
숫자나 포인터 타입에도 적용 가능하며, 저의 실제 코드베이스에서 발췌한 몇 가지 예시는 다음과 같습니다.
body := cmp.Or(page.Body, rawContent)
name := cmp.Or(jwt.Username(), "Almanack")
credits = append(credits, cmp.Or(credit.Name, credit.Byline))
metadata.InternalID = cmp.Or(
xhtml.InnerText(rows.Value("slug")),
xhtml.InnerText(rows.Value("internal id")),
metadata.InternalID,
)
scope.SetTag("username", cmp.Or(userinfo.Username(), "anonymous"))
currentUl = cmp.Or(
xhtml.Closest(currentUl.Parent, xhtml.WithAtom(atom.Ul)),
currentUl,
)
대부분의 경우 문자열에 대한 fallback 값을 제공하는 데 사용되지만, 마지막 예시에서는 non-nil *html.Node
를 찾는 데 사용되는 것을 볼 수 있습니다.
또한 cmp.Compare
함수와 함께 사용하여 다중 조건 비교를 구현할 수도 있습니다.
// ... (Order struct 및 orders slice 정의)
// Sort by customer first, product second, and last by higher price
slices.SortFunc(orders, func(a, b Order) int {
return cmp.Or(
cmp.Compare(a.Customer, b.Customer),
cmp.Compare(a.Product, b.Product),
cmp.Compare(b.Price, a.Price),
)
})
하지만 cmp.Or
함수는 short-circuit evaluation을 지원하지 않기 때문에, 첫 번째 조건이 참이더라도 나머지 조건들을 모두 평가하게 됩니다. 이는 성능 측면에서 고려해야 할 사항입니다.
cmp.Or
의 탄생 배경: 오랜 기다림 끝에 얻은 결실
cmp.Or
함수가 탄생하기까지는 오랜 시간이 걸렸습니다.
2016년 Stephen Kampmann이 strings.First
함수를 제안했지만, 당시에는 체계적인 제안 시스템이 없었기 때문에 제대로 논의되지 못했습니다.
2020년에는 제가 short-circuiting을 지원하는 ??
연산자를 제안했지만, Go 언어에 제네릭이 도입되면 제네릭 함수로 구현할 수 있다는 의견이 제시되었습니다.
제네릭이 Go 1.18 베타 버전에 추가된 후, 저는 reflect.Value.IsZero()
를 사용하여 zero 값 여부를 판단하는 truthy
패키지를 작성했지만, 리플렉션 사용으로 인한 성능 저하 문제가 발생했습니다.
결국 2022년에 comparable
타입에 대한 제네릭 함수인 cmp.Or
를 제안하게 되었고, 이 제안이 받아들여져 Go 1.22에 포함되었습니다.
cmp.Or
의 미래: 더욱 강력하고 유연한 Go 언어를 향하여
cmp.Or
함수는 Go 언어의 표현력과 유연성을 향상시키는 데 기여할 것입니다.
특히 제네릭과의 시너지를 통해 더욱 강력하고 안전한 코드 작성을 가능하게 할 것으로 기대됩니다.
앞으로 Go 언어가 어떻게 발전해 나갈지 기대하며, cmp.Or
함수가 Go 생태계에 긍정적인 영향을 미치기를 바랍니다.
참고:
- Go 1.22 Release Notes: https://tip.golang.org/doc/go1.22
- Proposal: Add cmp.Or: https://github.com/golang/go/issues/56211
- Discussion: Universal zero value: https://github.com/golang/go/discussions/57524
더 깊이 있는 학습을 위한 자료:
- Go Time Podcast Episode on cmp.Or: [관련 에피소드 링크]
- Understanding Go Generics: [제네릭 이해를 위한 자료 링크]
- The History of Go Proposals: [Go 제안 시스템의 역사에 대한 자료 링크]
마무리하며:
cmp.Or
함수는 Go 개발자들에게 매우 유용한 도구가 될 것입니다.
특히 조건부 값 선택이 필요한 상황에서 코드를 더욱 간결하고 명확하게 작성할 수 있도록 도와줄 것입니다.
'Go' 카테고리의 다른 글
Go 1.23 iter 패키지 완전 정복 (0) | 2024.08.24 |
---|---|
Go 1.23 이터레이터 완벽 정리 (0) | 2024.08.20 |
Go 1.22의 새로운 기능: slices.Concat (0) | 2024.07.29 |
Go 1.22의 새로운 기능: reflect.TypeFor (0) | 2024.07.29 |
Go 1.23의 숨겨진 보석: reflect.Value.Seq와 reflect.Value.Seq2 (0) | 2024.07.29 |