Lean으로 배우는 함수형 프로그래밍

6.6. 요약🔗

6.6.1. 모나드 결합하기🔗

모나드를 처음부터 작성할 때는 각 효과를 모나드에 추가하는 방식을 설명해 주는 디자인 패턴이 있습니다. 리더 효과는 모나드의 타입을 리더의 환경을 받는 함수로 만들어 추가합니다. 상태 효과는 초기 상태를 받아 값과 최종 상태의 쌍을 돌려주는 함수를 포함하여 추가합니다. 실패나 예외는 반환 타입에 합 타입을 포함하여 추가하고, 로깅이나 기타 출력은 반환 타입에 곱 타입을 포함하여 추가합니다. 기존 모나드를 반환 타입의 일부로 만들 수도 있으며, 이렇게 하면 기존 모나드의 효과를 새 모나드에 포함할 수 있습니다.

이러한 디자인 패턴은 어떤 기반 모나드에 효과를 추가하는 모나드 트랜스포머를 정의함으로써 재사용 가능한 소프트웨어 구성 요소의 라이브러리가 됩니다. 모나드 트랜스포머는 더 단순한 모나드 타입을 인자로 받아 기능이 강화된 모나드 타입을 반환합니다. 모나드 트랜스포머는 최소한 다음 인스턴스를 제공해야 합니다:

  1. 내부 타입이 이미 모나드라고 가정하는 Monad 인스턴스

  2. 내부 모나드의 액션을 변환된 모나드의 액션으로 옮기는 MonadLift 인스턴스

모나드 트랜스포머는 다형적 구조체나 귀납적 데이터 타입으로 구현할 수도 있지만, 대부분은 기반 모나드 타입을 확장된 모나드 타입으로 바꾸는 함수로 구현합니다.

6.6.2. 효과를 위한 타입 클래스🔗

흔히 쓰이는 디자인 패턴은 특정 효과를 구현할 때 그 효과를 지닌 모나드, 그 효과를 다른 모나드에 추가하는 모나드 트랜스포머, 그리고 그 효과에 대한 범용 인터페이스를 제공하는 타입 클래스를 함께 정의하는 것입니다. 이렇게 하면 프로그램에서는 필요한 효과만 명시하면 되므로, 호출자는 알맞은 효과를 지닌 모나드라면 무엇이든 제공할 수 있습니다.

보조적인 타입 정보(예: 상태를 제공하는 모나드에서의 상태 타입이나 예외를 제공하는 모나드에서의 예외 타입)는 출력 매개변수인 경우도 있고 그렇지 않은 경우도 있습니다. 출력 매개변수는 각 종류의 효과를 한 번씩만 사용하는 단순한 프로그램에서 가장 유용합니다. 하지만 한 프로그램에서 같은 효과의 인스턴스를 여러 개 사용할 때에는 타입 검사기가 너무 이른 시점에 잘못된 타입으로 확정해 버릴 위험이 있습니다. 따라서 보통은 두 버전을 모두 제공하며, 일반 매개변수를 사용하는 버전의 타입 클래스는 이름이 -Of로 끝납니다.

6.6.3. 모나드 트랜스포머는 교환 법칙이 성립하지 않습니다🔗

모나드에서 트랜스포머의 순서를 바꾸면 그 모나드를 사용하는 프로그램의 의미가 달라질 수 있다는 점에 유의해야 합니다. 예를 들어, StateT와 ExceptT의 순서를 바꾸면 예외가 발생할 때 상태 변경 사항을 잃는 프로그램이 될 수도 있고, 변경 사항을 유지하는 프로그램이 될 수도 있습니다. 대부분의 명령형 언어는 후자만 제공하지만, 모나드 트랜스포머가 제공하는 더 큰 유연성 때문에 당면한 작업에 알맞은 방식을 고르려면 신중하게 생각하고 주의를 기울여야 합니다.

6.6.4. 모나드 트랜스포머를 위한 do 표기법🔗

Lean의 do 블록은 블록을 어떤 값과 함께 종료하는 조기 반환, 지역적인 가변 변수, break와 continue를 쓸 수 있는 for 루프, 분기가 하나뿐인 if 문을 지원합니다. 이는 Lean으로 증명을 작성하는 데 방해가 될 명령형 기능을 도입하는 것처럼 보일 수 있지만, 실제로는 모나드 트랜스포머를 흔히 사용하는 몇 가지 방식을 더 편리하게 쓸 수 있게 해 주는 문법에 불과합니다. 내부적으로는 do 블록이 어떤 모나드로 작성되었든 ExceptT와 StateT를 적절히 사용해 그 모나드를 변환하며, 이를 통해 이러한 추가 효과를 지원합니다.