Phel v0.36.0: 숫자 타워와 일급 Var를 PHP에 도입

Phel, PHP로 컴파일되는 Clojure 스타일 Lisp가 버전 0.36.0을 출시했습니다. 이번 업데이트는 언어의 정밀도와 유연성에서 큰 도약을 의미하며, 강력한 숫자 타워와 일급 Var 핸들을 도입합니다. Lisp의 표현력을 PHP 생태계의 보편성과 결합하고자 하는 개발자들에게, 이번 변화는 고정밀 연산 및 고급 메타프로그래밍을 위한 핵심 도구를 제공합니다.

숫자 타워: 정밀도와 임의 규모

v0.36.0에서 가장 중요한 추가 사항 중 하나는 "숫자 타워" 구현으로, Phel이 표준 PHP 부동소수점 및 정수보다 훨씬 높은 정밀도로 숫자를 처리할 수 있게 됩니다.

유리수와 BigInteger

  • Exact Rationals: Phel은 이제 비율 리터럴(예: 1/2, -3/4)을 지원하며, 이는 직접 Rational 타입으로 파싱됩니다. 이는 분수에 대한 부동소수점 나눗셈에서 발생하는 반올림 오류를 없애줍니다.
  • BigInteger Support: 언어에 이제 임의 정밀도 정수를 위한 BigInteger가 포함되었습니다. 특히, PHP 정수가 연산(+, -, *, **) 중 오버플로우가 발생하면 자동으로 BigInteger로 승격되어 계산이 조용히 래핑되거나 정밀도를 잃는 일을 방지합니다.
  • BigDecimal: 고정 정밀도 소수 연산을 위해 BigDecimal이 이제 사용 가능하며, M 접미사 리터럴(예: 1.5M)을 활용합니다.

연산 동작 변경

이 타워를 지원하기 위해 여러 핵심 동작이 변경되었습니다. 특히, (/ int int)은 결과가 정수가 아닐 경우 기본적으로 부동소수점이 아니라 Rational을 반환합니다. 부동소수점 나눗셈이 필요한 경우에는 명시적으로 (double ...)를 사용하거나 피연산자 중 하나를 부동소수점으로 지정해야 합니다(예: (/ 1.0 2)).

일급 Var와 동적 바인딩

버전 0.36.0은 일급 Var 핸들을 도입하여 단순 심볼 해석을 넘어 변수 자체를 객체로 조작할 수 있게 합니다.

새로운 Var 기능

  • Var Handles: 새로운 (var sym) 특수 형태와 #'sym 리더 매크로를 통해 Var 핸들을 생성하고 조작할 수 있습니다.
  • Dynamic Rebinding: with-redefswith-bindings를 사용하면 개발자는 이제 맵에서 동적 var를 재바인딩할 수 있으며, 예외가 발생하더라도 시스템이 원래 값을 복원합니다.
  • Observability: PhelVarStateRegistry가 이제 "watches"를 지원하여, add-watchremove-watch를 사용해 alter-var-root를 통해 Var가 변경될 때 동작을 트리거할 수 있습니다.

확장된 타입 시스템 및 컬렉션

숫자와 var를 넘어, Phel은 현대 애플리케이션 요구에 맞게 네이티브 값 타입을 확장했습니다:

  • UUIDs: #uuid "...", random-uuid, parse-uuid를 통한 UUID 네이티브 지원.
  • Persistent Queues: 새로운 PersistentQueuepush, peek, pop 연산에 대해 평균 O(1) 성능을 제공하는 FIFO 구조를 제공합니다.
  • PHP Integration: PhpClass 값 타입이 이제 정규화된 PHP 클래스/인터페이스 Fully Qualified Names(FQN)를 래핑하여 Lisp와 PHP 간의 연결을 간소화합니다.
  • MapEntry: MapEntry 타입이 이제 2요소 벡터와 값으로 동등하게 되어 해시맵 반복 사용성을 향상시킵니다.

성능 및 도구 개선

이번 릴리스의 효율성 향상은 부팅 과정에서 개발자 경험(DX)에 크게 초점을 맞추고 있습니다:

  • Faster Boot Times: REPL과 테스트 러너가 이제 스캔 중에 vendor/, .git/, node_modules/와 같은 일반적인 대용량 디렉터리를 제외하며, 디렉터리 스캔은 프로세스당 메모이제이션됩니다.
  • Optimized Testing: phel test 명령에 이제 강력한 새로운 플래그가 포함되었습니다: --list는 테스트를 실행하지 않고 발견하고, --last-failed는 회귀를 반복하며, --slowest=N은 테스트 스위트에서 성능 병목을 식별합니다.
  • Namespace Standardization: 코어 라이브러리가 (use ...) 구문에 대해 점으로 구분된 클래스 FQN으로 전환되어 생태계 전반에 걸쳐 보다 일관된 구문을 제공합니다.

파괴적 변경 요약

v0.36.0으로 업그레이드하는 사용자는 몇 가지 파괴적 변화에 유의해야 합니다:

  1. Division: 결과가 정수가 아닐 경우 (/ int int)은 이제 float 대신 Rational을 반환합니다.
  2. UUIDs: uuid?는 이제 원시 문자열이 아니라 Phel\Lang\Uuid 객체만을 엄격히 허용합니다.
  3. Binding: binding 함수는 비동적 var에 대해 이제 InvalidArgumentException을 발생시키며, 대신 with-redefs를 사용해야 합니다.

Sources