---
title: "Herd: 모든 것이 값(value)인 프로그래밍 언어"
published: 2026-01-25T23:01:06.000Z
canonical: https://jeff.news/article/1182
---
# Herd: 모든 것이 값(value)인 프로그래밍 언어

모든 것이 pass-by-value인 인터프리터 언어 Herd. Copy-on-Write + 레퍼런스 카운팅으로 사이드 이펙트 없는 프로그래밍을 제공하고, 구조적으로 데이터 레이스가 불가능한 멀티스레딩을 지원함.

- **Herd**는 모든 것이 pass-by-value인 인터프리터 언어임. 리스트든 딕셔너리든 함수에 넘기면 원본이 절대 변하지 않음. 사이드 이펙트 걱정 없이 로컬에서 마음대로 수정 가능

- 핵심 트릭은 **Copy-on-Write + 레퍼런스 카운팅** 조합임:
  - 참조가 1개뿐이면 → 그냥 in-place 수정 (복사 안 함)
  - 참조가 2개 이상이면 → 얕은 복사 후 수정. 복사본은 참조 카운트가 1이니까 이후 수정은 다시 in-place
  - 이 방식의 보너스: **참조 순환(reference cycle)이 구조적으로 불가능**해서 GC에 사이클 탐지가 필요 없음

- Swift의 "Mutable Value Semantics"와 비슷한 아이디어지만, Swift는 정적 타입이고 훨씬 복잡함. Matlab/R도 배열에 COW를 쓰지만 참조 카운트가 1로 돌아왔을 때 안정적으로 추적을 못 해서 불필요한 복사가 더 잦음. PHP는 배열만, Perl은 보장을 깨는 방법이 너무 많음

## 언어 특징

- 타입: `()` (유닛), `bool`, `number` (f64), `string`, `list`, `dict`, `function`
- `var`로 선언해야만 뮤터블. 기본은 이뮤터블. 수정은 `set` 키워드 필요
- 함수 문법이 독특함: `\x y\ x + y` 같은 식. Rust 클로저(`|x|`)에서 `|` 대신 `\`를 쓰고 ML 스타일 호출(`f x y`)을 섞은 느낌
- 파이프 연산자 `|`로 체이닝 가능, `|=`로 in-place 수정도 됨
- 패턴 매칭, 디스트럭처링(`!`), switch 표현식 지원

## 멀티스레딩이 되는데 데이터 레이스가 불가능

- `Parallel.parallelMap`, `Parallel.parallelRun` 같은 빌트인 멀티스레딩 지원
- 모든 뮤테이션이 현재 스레드의 복사본에만 영향을 주니까 **데이터 레이스가 구조적으로 불가능**함. 복잡한 데이터 구조를 동기화 걱정 없이 스레드 간 전달 가능. 다만 통신은 리턴 값으로만 가능

## 구현과 성능

- 싱글패스 JIT 컴파일러로 런타임에 머신코드 생성, 백엔드는 **Cranelift** 사용
- 값 표현은 **NaN-boxing** 방식이라 primitive 타입은 힙 할당 없이 처리됨
- CPython보다는 빠르고, Node.js보다는 느린 수준. "모던 JS 런타임급은 아니지만 인터프리터 언어치곤 경쟁력 있음"
- 현재 성능 병목: 아토믹 레퍼런스 카운팅 오버헤드 (특히 핫 루프), 싱글패스 JIT의 최적화 한계, 임포트 시 전체 컴파일 오버헤드

> [!NOTE]
> 취미 프로젝트라 프로덕션 사용은 비추라고 본인이 직접 말하고 있음. 하지만 "동적 타입 언어에서 mutable value semantics를 제대로 탐구한 건 처음"이라는 주장은 꽤 설득력 있음

## 핵심 포인트

- 모든 타입이 pass-by-value, COW + 레퍼런스 카운팅으로 불필요한 복사 최소화
- 참조 순환이 구조적으로 불가능해서 GC에 사이클 탐지 불필요
- 데이터 레이스가 불가능한 빌트인 멀티스레딩
- Cranelift 기반 싱글패스 JIT로 CPython보다 빠른 성능
- 동적 타입 언어에서 mutable value semantics를 본격 탐구한 최초 사례

## 인사이트

Swift의 value semantics를 동적 타입 언어로 가져온 실험. 프로덕션용은 아니지만 '값 의미론으로 모든 걸 해결하면 어떻게 될까?'라는 질문에 대한 깔끔한 답변.
