Composing Programs - 6

Higher-Order Functions
우리는 함수가 인자의 특정 값과 독립적으로 복합 연산을 기술하는 추상화의 방법임을 살펴보았다. 즉 square 함수에서

우리는 특정 숫자의 제곱에 관해 이야기하는 것이 아니라, 어떤 숫자든 그 제곱을 구하는 방법에 대해 이야기 하고 있는 것이다. 물론 다음과 같은 표현식을 매번 작성함으로써 이 함수를 정의하지 않고도 지낼 수는 있다.

그리고 square라는 이름을 명시적으로 언급하지 않을 수도 있다. 이러한 방식은 square같은 단순한 계산에는 충분하겠지만, abs나 fib와 같은 더 복잡한 예시에서는 매우 힘든일 이 될 것이다. 일반적으로 함수 정의가 부족하다면, 우리는 더 높은 수준의 연산 간점에서 생각하지 못하고 언어에 내장된 기본 연산 수준에서만 항상 작업해야 하는 일에 처하게 된다. 우리의 프로그램은 제곱을 계산할 수는 있겠지만, 우리의 언어는 제곱이라는 개념을 표현할 능력을 상실하게 된다.
강력한 프로그래밍 언어에 우리가 요구해야 할 조건 중 하나는, 공통된 패턴에 이름을 붙여 추상화를 구축하고 그 이름들을 통해 직접 작업할 수 있는 능력이다. 함수가 바로 이러한 능력들을 제공한다. 이어지는 예제들에서 보게 되겠지만, 코드에는 반복해서 나타나지만 여러가지 서로 다른 함수와 함께 사용되는 공통적인 프로그래밍 패턴들이 존재한다. 이러한 패턴들 역시 이름을 붙여 추상화할 수 있다.
특정한 일반적 패턴을 이름있는 개념으로 표현하기 위해, 우리는 다른 함수를 인자로 받거나 함수를 리턴값으로 반환할 수 있는 함수를 구축해야 할 필요가 있다. 함수를 다루는 함수를 고차함수라고 한다.
이 장에서는 고차 함수가 어떻게 강력한 추상화 매커니즘으로 기능하며, 우리 언어의 표현력이 얼마나 방대하게 향상시킬 수 있는지 보여줄 것이다.
Functions as Arguments
모두 합계를 계산하는 다음 세가지 함수를 살표보자.
처음 sum_naturals는 n깢의 자연수의 합을 계사한다.

두 번째 sum_cubes는 n까지의 자연수의 세제곱의 합을 계산한다.

세 번째 pi_sum은 아래 급수의 항들의 합을 계산하며, 이 급수는 아주 천천히 pi값으로 수렴한다.
$$\frac{8}{1 \cdot 3} + \frac{8}{5 \cdot 7} + \frac{8}{9 \cdot 11} + \dots$$

이 3가지 함수는 명확하게 공통된 기본 패턴을 공유하고 있다. 대부분의 코드가 동일하며, 오직 이름과 더해질 항을 계산하는데 사용되는 k에 대한 함수만이 다를 뿐이다. 우리는 동일한 템플릿의 빈칸을 채움으로써 각 함수를 만들어낼 수 있다.

이러한 공통 패턴의 존재는 표면으로 끌어올려지기를 기다리는 유용한 추상화가 있다는 강력한 증거이다. 이 함수들은 각각 항들의 합계이다. 프로그램 설계자로서 우리는 단순히 튻정 합계를 계산하는 함수 뿐 아니라 합계라는 개념 자체를 표혐하는 함수를 작성할 수 있을 만큼 우리의 언어가 강력하기를 바란다. 파이썬에서는 위에서 보여준 공통 템플릿을 가져와서 빈칸들을 형식 매개변수로 변환함으로써 이를 쉽게 수행할 수 있다.
아래 예제에서 summation은 상한선인 n과 k번째 항을 계산하는 함수 term을 2개의 인자로 받는다.
우리는 summation을 여느 함수처럼 사용할 수 있으며, 이는 합계를 간결하게 표현해준다. 이 예제 실행 과정을 천천히 따라가보면서, cube를 로컬 이름인 term에 바인딩하는 것이 어떻게 1^3 + 2^3 + 3^3 이라는 결과를 정확하게 계산해내는지 확인해보자.

인자를 그대로 반환하는 항등 함수를 사용하면 정확한 동일한 summation 함수를 사용하여 자연수의 합도 구할 수 있다.

summation 함수는 특정 수열을 위한 별도의 함수를 정의하지도 않고 직접 호출 될 수도 있다.

우리는 각 항을 계산하는 pi_term 함수를 정의함으로써 summation 추상화를 이용해 pi_sum을 정의할 수 있다. pi에 근사한 겂을 얻기 위해 1*10^6 = 1000000의 축약형인 1e6을 인자로 전달한다.

Functions as General Methods
우리는 수치 연산의 패턴을 추상화하여 특정 숫자와 독립적으로 만들기 위한 매커니즘으로 사용자 정의 함수를 도입했다. 고차 함수를 통해 더 강력한 종류의 추상화를 보기 시작한다. 즉, 어떤 함수들은 자신이 호출하는 특정 함수와 독립적으로 계산의 일반적인 방법을 표현한다.
함수의 의미가 이처럼 개념적으로 확정되었음에도 불구하고, 호출 표현식을 평가하는 우리의 환경 모델은 아무런 변경 없이 고차 함수의 경우에도 매끄럽게 확장된다. 사용자 정의 함수가 어떤 인자들에 적용될 때, 형식 매개변수들은 새로운 로컬 프레임 안에서 그 인자값들에 바인딩된다.
반복적 개선을 위한 일반적인 방법을 구현하고, 이를 이용해 황금비를 계산하는 아래 예제를 살펴보자. 흔히 phi라고 불리는 황금비는 자연, 예술, 건축에서 자주 등장하는 1.6 근처의 숫자이다.
반복적 개선 알고리즘은 방정식 해에 대한 추측에서 시작한다. 이 알고리즘은 추측을 개선하기 위해 업데이트 함수를 반복적으로 적용하고, 현재의 추측이 정답이라고 간주될 만큼 "충분히 가까운 지" 확인하기 위해 근사 비교를 적용한다.

이 improve 함수는 반복적인 정밀화 과정을 일반화하여 표현한 것이다. 이 함수는 어떤 문제가 해결되고 있는지 명시하지 않는다. 그러한 세부 사항은 인자로 전달되는 update와 close 함수에 맡겨진다.
황금비의 잘 알려진 성질 중에는, 임의의 양수에 역수에 1을 더하는 과정을 반복하여 계산할 수 있다는 것과, 황금비의 제곱이 황금비에 1을 더한 값과 같다는 성질이 있다. 우리는 이러한 성질들을 improve와 함께 사용할 함수들로 표현할 수 있다.

위에서 우리는 인자들이 서로 대략적으로 같은 경우 True를 반환하도록 의도된 approx_eq 호출을 도입했다. apporx_eq를 구현하기 위해, 우리는 두 숫자 사이의 차이의 절대값을 작은 허용 오차 값과 비교할 수 있다.

golden_update와 square_close_to_successor를 인자로 하여 improve를 호출하면 황금비에 대한 유한한 근사치를 계산할 것이다.

평가 단계를 추적함으로써 이 결과가 어떻게 계산되는지 알 수 있다. 먼저 update,close,guess에 대한 바인딩을 가진 improve의 로컬 프레임이 구성된다.
improve의 본문에서 close라는 이름은 square_close_to_successor에 바인딩 되며, 이는 guess의 초기값에 대해 호출된다.

이 예제는 컴퓨터 과학의 두 가지 관련 있는 큰 아이디어를 보여준다.
첫째, 명명(naming)과 함수는 방대한 양의 복잡성을 추상화할 수 있게 해준다. 각각의 함수 정의는 매우 단순했지만, 우리의 평가 절차에 의해 시작된 계산 과정은 상당히 복잡하다.
둘째, 파이썬 언어를 위한 매우 일반적인 평가 절차 덕분에 작은 구성 요소들이 복잡한 과정으로 조합될 수 있다는 사실이다. 프로그램을 해석하는 절차를 이해하는 것은 우리가 만든 과정을 검증하고 조사할 수 있게 해준다.
언제나 그렇듯, 우리의 새로운 일반적 방법인 improve는 정확성을 확인하기 위한 테스트가 필요하다. 황금비는 이 반복적인 결과와 비교할 수 있는 정확한 폐쇄형 해를 가지고 있기 때문에 그러한 테스트를 제공할 수 있다.

이 테스트의 경우 무소식이 희소식이다. improve_test는 assert문이 성공적으로 실행된 후 None을 반환한다.
Defining Functions III : Nested Definitions
위 예제들은 함수를 인자로 전달하는 능력이 프로그래밍 언어의 표현력을 얼마나 크게 향상시키는지 보여준다. 각각의 일반적인 개념이나 방벙식은 그에 해당하는 짧은 함수로 매핑된다.
하지만 이러한 접근 방식의 한 가지 부정적인 결과는 전역 프레임이 작은 함수들의 이름으로 가득 차게 되고, 이 이름들이 모두 중복되지 않아야 한다는 점이다. 또 다른 문제는 우리가 특정 함수 시그니처에 제약을 받는다는 점이다. 예를 들어, improve의 update 인자는 반드시 정확히 하나의 인자만을 받아야 한다. 중첩된 함수 정의는 이 두 가지 문제를 모두 해결하지만, 우리의 환경 모델을 더 풍부하게 확장할 것을 요구한다.
새로운 문제인 숫자의 제곱근 계산을 생각해보자. 프로그래밍 언어에서 "square root"는 흔히 sqrt로 줄여 쓴다. 다음 업데이트 함수를 반복적으로 적용하면 a의 제곱근으로 수렴할 것이다.

이 2개의 인자를 받는 업데이트 함수는 improve와 호환되지 않으며 (인자를 2개 받기 때문), 단일 업데이트만 제공할 뿐이다. 우리가 정말로 원하는 것은 반복적인 업데이트를 통해 제곱근을 구하는 것이다. 이 두 문제를 모두 해결하는 방법은 함수 정의를 다른 정의의 본문 안에 배치하는 것이다.

로컬 할당과 마찬가지로 로컬 def 문은 현재의 로컬 프레임에만 영향을 미친다. 이 함수들은 sqrt가 평가되는 동안에만 scope에 존재한다. 우리의 평가 절차와 일관되게, 이러한 로컬 def 문들은 sqrt가 호출되기 전까지 평가조차 되지 않는다.
어휘적 스코프 (Lexical scope)
로컬로 정의된 함수는 자신이 정의된 스코프 내의 이름 바인딩에도 접근할 수 있다. 이 예제에서 sqrt_update는 자신을 감싸고 있는 함수 sqrt의 형식 매개변수인 a라는 이름을 참조한다. 이처럼 중첩된 정의들 사이에서 이름을 공유하는 방식을 어휘적 스코핑이라 한다. 결정적으로, 내부 함수들은 자신이 호출된 곳이 아니라, 정의된 환경의 이름들에 접근할 수 있다.
어휘적 스코핑을 가능하게 하려면 환경 모델에 2가지 확장이 필요하다.
- 모든 사용자 정의 함수는 부모 환경을 가진다. 이는 함수가 정의된 환경이다.
- 사용자 정의 함수가 호출되면, 그 로컬 프레임은 자신의 부모 환경을 확장한다.
sqrt 이전까지 모든 함수는 전역 환경에서 정의되었으므로 모두 전역 환경이라는 동일한 부모를 가졌다. 반면 파이썬이 sqrt 내의 처음 2 절을 평가할 때, 로컬 환경과 연관된 함수들을 생성한다.

위 호출에서 환경은 먼저 sqrt를 위한 로컬 프레임을 추가하고, sqrt_update와 sqrt_close에 대한 def문을 평가한다.

그 후 sqrt_update라는 이름은 이 새롭게 정의된 함수로 해석되어 improve의 인자로 전달된다. improve 본문 내부에서 우리는 sqrt_update에 바인딩 된 업데이트 함수를 초기 추측값인 x=1에 적용해야 한다. 이 마지막 적용은 sqrt_update를 위한 환경을 생성하는 이 환경은 x만을 포함하는 로컬 프레임으로 시작하지만, 부모 프레임인 sqrt(f1)가 여전히 a에 대한 바인딩을 가지고 있는 상태이다.
이 평가 절차에서 가장 중요한 부분은 sqrt_update의 부모가 sqrt_update 호출로 생성된 프레임으로 전달된다는 점이다.
확장된 환경 (Extended Environments)
환경은 임의의 길이를 가진 프레임들의 체인으로 구성될 수 있으며, 항상 전역 프레임으로 끝난다. 이 sqrt 예제 이전까지 환경은 최대 2 개의 프레임 (로컬 프레임, 전역 프레임)으로만 이루어졌다.
중첩된 def문을 통해 다른 함수 내부에서 정의된 함수를 호출함으로써 우리는 더 긴 체인을 만들 수 있다. 이번 sqrt_update 호출을 위한 환경은 세 개의 프레임으로 구성된다. 로컬 sqrt_update 프레임, sqrt_update가 정의된 sqrt프레임, 전역 프레임
sqrt_update 본문의 반환 표현식은 이 프레임 체인을 따라감으로써 a값을 찾아낼 수 있다. 이름을 조회할 때는 현재 환경에서 해당 이름에 바인딩된 첫번째 값을 찾는다. 파이썬은 먼저 sqrt_update 프레임을 확인하지만 a가 없다. 다음으로 부모 프레임인 f1을 확인하여 a가 256에 바인딩 된 것을 찾아낸다.
이를 통해 파이썬에서 어휘적 스코핑이 갖는 2가지 핵심 이점을 깨닫게 된다.
- 로컬 함수의 이름은 외부의 이름과 간섭하지 않는다. 로컬 함수 이름은 전역환경이 아니라 그것이 정의된 당시의 로컬 환경에 바인딩되기 때문이다.
- 로컬 함수는 자신을 감싸는 함수의 환경에 접근할 수 있다. 로컬 함수의 본문은 자신이 정의된 평가 환경을 확장하는 환경에서 평가되기 때문이다.
sqrt_update 함수는 자신이 정의된 환경에서 참조된 a 값이라는 일종의 데이터를 가지고 다닌다. 이처럼 정보를 감싸고 있기 때문에, 로컬로 정의된 함수들을 흔히 클로저라 부른다.
Functions as Returned Values
리턴 값 자체가 함수인 함수를 생성함으로써 우리는 프로그램에서 훨씬 더 강력한 표현력을 얻을 수 있다. 어휘적 스코프를 사용하는 프로그래밍 언어의 중요한 특징은, 로컬에서 정의된 함수가 반환될 때 자신의 부모 환경을 그대로 유지한다는 점이다. 다음 예제는 이 기능의 유용성을 잘 보여준다.
많은 단순한 함수들이 정의되고 나면, 함수 합성은 우리 프로그래밍 언어에 포함할 수 있는 아주 자연스러운 결합 방법이 된다. 즉 두 함수 f(x) g(x)가 주어졌을 때, 우리는 h(x) = f(g(x))를 정의하고 싶을 수 있다. 기존에 가진 도구들을 이용해 다음과 같이 함수 합성을 정의할 수 있다.

이 예제의 환경 다이어그램은 이름 충돌이 발생하는 상황에서도 f와 g라는 이름이 어떻게 정확하게 해석되는지 보여준다.

compose1 에서 숫자 1은 합성되는 함수들이 모두 단일 인자를 받는다는 것을 의미한다. 이러한 명명 규칙은 인터프리터에 의해 강제되는 것은 아니며 1은 단지 함수 이름의 일부일 뿐이다.
이 시점에서 우리는 계산의 환경 모델을 정밀하게 정의하기 위해 들인 노력의 결실을 보기 시작한다. 함수를 이런 방식으로 반환하는 능력을 설명하기 위해 기존 환경 모델을 수정할 필요가 전혀 없기 때문이다.
Example : Newton's Method
이 확장된 예제는 함수의 반환 값과 로컬 정의가 어떻게 결합하여 일반적인 아이디어를 간결하게 표현할 수 있는지 보여준다. 우리는 머신러닝, 과학 계산, 하드웨어 설계 및 최적화 분야에서 널리 사용되는 알고리즘을 구현해 볼 것이다.
Newton's Method는 수학 함수의 결과값이 0이 되도록 만드는 인자를 찾아내기 위한 고전적인 반복 접근법이다. 이러한 값들을 함수의 영점이라고 부른다. 함수의 영점을 찾는 것은 제곱근 계산과 같은 우리가 관심을 두는 다른 문제를 해결하는 것과 종종 동일한 의미를 갖는다.
본격적인 시작에 앞서 한 가지 언급하자면, 우리가 제곱근을 계산하는 방법을 당연하게 여기기 쉽다는 점이다. 파이썬 뿐만 아니라 우리의 스마트폰, 웹 브라우저, 공학용 계산기도 이를 대신해 줄 수 있다. 하지만 컴퓨터 과학을 배우는 과정 중 하나는 이러한 수치들이 어떻게 계산되는지 이해하는 것이며, 여기서 제시하는 일반적인 접근 방식은 파이썬에 내장된 기능들을 넘어 아주 방대한 종류의 방정식을 푸는데 적용될 수 있다.
Newton's method는 일련의 반복적 개선 알고리즘이다. 이 방법은 미분 가능한 모든 함수의 영점에 대한 추측값을 개선해 나간다. Newton's method는 이러한 선형 근사를 따라가며 함수의 영점을 찾는다.
지점 (x, f(x))를 지나면서 그 지점의 함수 f(x) 곡선과 동일한 기울기를 가진 직선을 상상해보자. 이러한 직선을 접선이라 부르며, 그 기울기를 x에서의 f의 미분계수라고 한다.
이 직선의 기울기는 함수 인자 변화량에 대한 함수 값의 변화량의 비율이다. 따라서 x에서 함수값 f(x)를 기울기로 나눈 값만큼 x를 이동시키면 이 접선이 0과 만나는 지점의 인자 값을 얻을 수 있다.

newton_update 함수는 함수 f와 그 미분 함수 df에 대해, 이 접선을 따라 0으로 향해가는 계산 과정을 표현한다.

마지막으로 우리는 newton_update와 이전에 만든 improve 알고리즘, 그리고 f(x)가 0에 가까운지 확인하는 비교 함수를 사용하여 find_zero 함수를 정의할 수 있다.

거듭제곱근 계산하기
뉴턴 방법을 사용하면 임의의 n차 거듭제곱근을 계산할 수 있다. a의 n차 거듭제곱근은 x를 n번 곱했을 때 a가 되는 값 x이다. 예를 들면,
- 64의 제곱근은 8이다 (8*8 = 64)
- 64의 세제곱근은 4이다 (444 = 64)
- 64의 여섯제곱근은 2이다 (22222*2 = 64)
다음과 같은 관차을 통해 뉴턴 방법으로 거듭제곱근을 계산할 수 있다.
- 64의 제곱근은 x^2 - 64 = 0을 만족하는 x값이다.
- 더 일반적으로 a의 n차 거듭제곱근은 x^n - a = 0을 만족하는 x 값이다.
만약 이 마지막 방정식의 영점을 찾을 수 있다면, 우리는 n차 거듭제곱근을 계산할 수 있다.

우리는 먼저 f와 그 미분 함수 df를 정의하여, square_root를 구현한다. 미적분학에서 f(x) = x^2 - a의 미분은 선형 함수 df(x) = 2x라는 사실을 이용한다.

이를 임의의 n차 거듭제곱근으로 일반화하면 f(x) = x^n- a와 그 미분 함수 df(x) = (n-1)x를 계산하게 된다.

이러한 모든 계산의 근사 오차는 approx_eq의 허용 오차를 더 작은 숫자를 변경함으로써 줄일 수 있다.
Newton's method를 실험할 때 유의할 점은 이 방법이 항상 수렴하지는 않는다는 것이다. improve의 초기 추측값은 영점에 충분히 가까워야 하며, 함수에 대한 다양한 조건이 충족되어야 한다. 이러한 단점에도 불구하고, Newton's method는 미분 가능한 방정식을 풀기 위한 강력하고 일반적인 계산 방법이다. 현대 컴퓨터에서 로그 계산이나 큰 정수의 나눗셈을 수행하는 매우 빠른 알고리즘들도 이 기술의 변형된 형태를 사용한다.
Currying
우리는 고차 함수를 사용하며 여러 개의 인자를 받는 함수를 각각 하나의 인자만 받는 함수들의 체인으로 변환할 수 있다. 구체적으로 함수 f(x, y)가 주어졌을 때 g(x)(y)가 g(x, y)와 동일하게 작동하도록 하는 함수 g를 정의할 수 있다. 여기서 g는 단일인자 x를 받아 또 다른 단일인자 y를 받는 함수를 반환하는 고차함수이다. 이러한 변화를 currying이라 부른다.
예를 들어, pow 함수의 커링 버전을 다음과 같이 정의할 수 있다.

Haskell과 같은 일부 프로그래밍 언어는 단일 인자를 받는 함수만을 허용하므로, 프로그래머는 모든 다중 인자 프로시저를 커링해야 한다. 파이썬과 같이 더 일반적인 언어에서 커링은 단일 인자만을 받는 함수가 필요한 상황에서 유용하다.
예를 들어 map 패턴은 단일 인자 함수를 일련의 값들에 적용한다. 나중 장에서 맵 패턴의 더 일반적인 사례들을 보게 되겠지만, 지금은 다음과 같이 간단하게 구현해 볼 수 있다.

우리는 2의 거듭제곱을 구하는 함수를 따로 작성하는 대신, map_to_range와 curried_pow를 사용하여 처음 10개의 2의 거듭제곱 값을 계산할 수 있다.

동일한 두 함수를 사용하여 다른 숫자의 거듭제곱도 계산할 수 있다. 커링을 이용하면 거듭제곱을 구하려는 각 숫자마다 별도의 함수를 작성할 필요가 없다.
위의 예제들에서는 pow 함수를 직접 커링하여 curried_pow를 얻었다. 대신, 커링 변환과 그 반대 과정인 언커링 변환을 자동화하는 함수를 정의할 수도 있다.

curry2 함수는 2개의 인자를 받는 함수 f를 받아 하나의 인자를 받는 함수 g를 반환한다. g가 인자 x에 적용되면, 단일 인자 함수 h를 반환한다. h가 y에 적용될 때 비로소 f(x, y)를 호출한다. 따라서 curry2(f)(x)(y)는 f(x, y)와 동일하다. uncurry2 함수는 커링 변환을 역으로 수행하므로, uncurry2(curry2(f))는 f와 동일하게 작동한다.

Lambda Expressions
지금까지 우리는 새로운 함수를 정의하고 싶을 때마다 매번 이름을 부여해야 했다. 하지만 다른 유형의 표현식에서는 중간 단계의 값들에 굳이 이름을 붙일 필요가 없다.
예를 들어 ab나 cd 혹은 전체 표현식에 이름을 붙이지 않고도 ab + cd를 계산할 수 있는 것과 같다. 파이썬에서는 람다 표현식을 사용하여 즉석에서 함수 값을 생성할 수 있으며 이는 이름이 없는 함수로 평가된다. 람다 표현식은 단일 반환 표현식만을 본문으로 가지는 함수로 평가된다. 할당문이나 제어문은 허용되지 않는다.

람다 표현식의 구조는 그에 대응하는 영어문장을 만들어봄으로써 이해할 수 있다.
lambda x : f(g(x))
"x를 받아 f(g(x))를 반환하는 함수"
람다 표현식의 결과물을 람다 함수라고 부른다. 여기에는 본래의 이름이 없기 때문에 그 점을 제외하면 다른 함수와 동일하게 작동한다.

환경 다이어그램에서 람다 표현식의 결과 또한 함수이며, 그리스 무낮로 이름이 표기된다. 앞서 다룬 compose 예제는 람다 표현식을 사용하여 매우 간결하게 표현될 수 있다.

일부 프로그래머들은 람다 표현식을 통한 무명 함수 사용이 더 짧고 직접적이라 생각한다. 그러나 복합적인 람다 표현식은 그 간결함에도 불구하고 가독성이 떨어지는 것으로 악명이 높다. 다음 정의는 문법적으로 정확하지만, 많은 프로그래머가 이를 빠르게 이해하는 데 어려움을 겪는다.

일반적으로 파이썬 스타일은 람다 표현식보다 명시적인 def문을 선호하지만, 인자나 반환 값으로 간다난 함수가 필요한 경우에는 람다를 허용한다.
이러한 스타일 규칙은 단지 지침일 뿐이며 우리는 원하는 방식으로 프로그래밍할 수 있다. 하지만 프로그램을 작성할 때, 언젠가 여러분의 프로그램을 읽게 될 사람을 생각해보자. 프로그램을 이해하기 쉽게 만들 때, 우리는 그들에게 호의를 베푸는 것이다.
람다라는 용어는 수학적 표기법과 초기 조판 시스템의 제약 사이의 불일치로 인해 발생한 역사적 사고이다. 비록 그 어원은 특이하지만, 람다 표현식과 그에 대응하는 함수 적용을 위한 형식 언어인 람다 대수는 파이썬 커뮤니티를 넘어 컴퓨터 과학 전반에서 공유되는 근본적인 개념이다.
Function Decorators
파이썬은 def문을 실행할 때 고차 함수를 적용할 수 있는 데코레이터라는 특별한 구문을 제공한다. 가장 흐난 예시는 아마도 함수 호출을 추적하는 trace 일 것이다.

이 예제에서 고차 함수 trace가 정의되었다. 이 함수는 인자로 받은 함수를 호출하기 전에 그 인자를 출력하는 print 문을 먼저 실행하는 새로운 함수를 반환한다. triple 함수를 정의하는 def문에 @trace라는 주석이 달려있는데, 이는 def의 실행 규칙에 영향을 미친다.
평소와 같이 triple이라는 함수 자체는 생성되지만, triple이라는 이름이 방금 생성된 그 함수에 바인딩 되지는 않는다. 대신 triple이라는 이름은 새로 정의된 함수를 인자로 하여 trace를 호출했을 때 반한되는 함수 값에 바인딩된다. 코드로 표현하자면, 이 데코레이터는 다음과 같은 코드와 동일하다.
