Phunctional: PHP에서 함수형 프로그래밍을?
내가 PHP에 가지는 불만들 가운데 하나는 함수가 항상 전역적인 이름을 가져야만 한다는 것이다. 표준 함수라고 기껏 존재하는 create_function()은 lambda_1, lambda_2와 같이 역시나 이름을 가지는 함수를 리턴한다.
게다가 함수가 값이 아니고, 나아가 그것을 가리키는 적절한 타입조차 따로 존재하지 않는다. 대신 함수 이름의 문자열에 호출 연산자—()—가 붙으면 해당 이름의 함수를 찾아 호출한다. 뒤에 OO가 붙으며 인스턴스 메서드나 클래스 메서드에 대한 콜백도 필요해지면서 아래와 같은 웃긴 편법이 사용되기 시작했다.
$instance_method = array($instance, 'method_name');
$instance_method();
$class_method = array('ClassName', 'method_name');
$class_method();
평소 PHP에서 함수를 사용하며 느꼈던 불편들과, 바램들을 종합하여 Phunctional이라는 라이브러리를 작성해 보았다. 이번에 처음으로 공개하는 것인데, 앞으로도 계속 발전시킬 생각이다.
라이브러리 안에는 Functor, Lambda, Range, iterator-tools 등이 들어있는데, 그 중에서 가장 공들인 부분은 Lambda였다. 라이브러리를 공개한 이유도 일단 돌아가는 Lambda를 뽑아내서이다. 본래는 진정한 익명 함수를 지원하기 위해 내부적으로 PHP에서의 최소 단위 인스트럭션을 모두 분리 제공하는 방식으로 구현했는데, 디자인 자체는 썩 만족스러웠지만, 그놈의 속도가 절대 써먹지 못할 정도여서 포기했다. 다시 작성한 버전에는 렉시컬 클로징(lexical closing)을 어설프나마 추가하고—객체의 경우에는 참조가 되기 때문에 자동으로 클로즈가 되지만, 프리미티브 타입(primitive types)의 경우에는 참조 연산자를 명시적으로 붙여야 클로즈가 된다—내부적으로 표현식 코드 스트링을 생성하여 create_function()으로 넘기는 방식으로 구현을 했다. 때문에 속도는 그럭저럭 봐줄만 하지만, 엄밀한 의미에서의 익명 함수는 아니다.
현재는 아래에 설명하는 Lambda 구현을 대체하는 새로운 구현이 Phunctional에 도입되었다. 사용법도 전혀 다르고, 새로운 구현이 훨씬 깔끔하고 직관적이니 참고 바란다. PHP에서 람다 구현, Phunctional Lambda, 함수 내에서 지역 변수 클로즈하기, Phunctional Lambda: 이제 고차 함수 된다
operator 확장을 설치한 경우에는 아래와 같이 제법 깔끔하게 사용할 수 있다. (S(0) 대신 $_0을 사용해도 된다.)
assert(map(S(0) + 0.12, array(1, 2, 3, 4))
== array(1.12, 2.12, 3.12, 4.12));
하지만, 설치 안했으면 추악한 코드가 나온다는 것! (어쩔 수 없는 한계…….)
assert(map(S(0)->__sub(1), array(1, 2, 3, 4))
== array(0, 1, 2, 3));
대신 간단한 곳에서는 RawLambda를 사용하는 것으로 그럭저럭 타협 가능하다. PHP의 변수명이 달러 바로 뒤에 숫자가 오지 못한다는 것에 착안하여, 그것을 인자 표현식으로 사용하게 만들었다.
assert(map(lambda('$0 * 2'), array(1, 2, 3, 4))
== array(2, 4, 6, 8));
PHP에서 DSEL을 작성하는 등 메타프로그래밍(metaprogramming)을 해볼 때마다 느끼는 것인데, 언어 자체의 어쩔 수 없는 한계라고 느끼는 것이 있다면, 그것이 현재의 내 실력으로 작성할 수 있는 코드의 한계와 일맥상통하는 것 같다. 내가 좀더 나은 코드를 작성할 수 있게 되면, 그 순간 언어의 한계라고 생각했던 것이 어느새 보이지 않게 되며 문제들이 명쾌하게 해결되는 것을 종종 보기 때문이다.

January 24th, 2007 at 9:04 PM
RawLambda는 어디까지 복잡해질 수 있나요?
January 24th, 2007 at 11:20 PM
표현식 문자열을 단순히 치환하는 정도이기 때문에, 아무리 복잡한 표현식이라도 가능해. 근데 반대로 단순 치환이라서,
lambda('$0.\'i am $7ring\'')과 같은 경우에는 오작동하겠지.January 28th, 2007 at 8:45 PM
[…] 내가 만든 Phunctional의 Lambda를 사용하면 쓸만하다. […]