4.6. 추가 편의 기능
4.6.1. 공유 인자 타입
같은 타입의 인자를 여러 개 받는 함수를 정의할 때는 그 인자들을 하나의 콜론 앞에 함께 쓸 수 있습니다. 예를 들어,
def equal? [BEq α] (x : α) (y : α) : Option α :=
if x == y then
some x
else
none다음과 같이 작성할 수 있습니다
def equal? [BEq α] (x y : α) : Option α :=
if x == y then
some x
else
none이는 타입 시그니처가 클 때 특히 유용합니다.
4.6.2. 선행 점 표기법
귀납적 타입의 생성자는 네임스페이스 안에 있습니다. 덕분에 서로 관련된 여러 귀납적 타입이 같은 생성자 이름을 쓸 수 있지만, 프로그램이 장황해질 수 있습니다. 해당 귀납적 타입이 무엇인지 알려진 문맥에서는 생성자 이름 앞에 점을 붙여 네임스페이스를 생략할 수 있으며, Lean은 기대 타입을 사용해 생성자 이름을 해석합니다. 예를 들어, 이진 트리를 좌우 반전하는 함수는 다음과 같이 작성할 수 있습니다.
def BinTree.mirror : BinTree α → BinTree α
| BinTree.leaf => BinTree.leaf
| BinTree.branch l x r => BinTree.branch (mirror r) x (mirror l)네임스페이스를 생략하면 프로그램이 상당히 짧아지지만, 그 대신 Lean 컴파일러가 없는 코드 리뷰 도구 같은 환경에서는 프로그램을 읽기가 더 어려워집니다:
def BinTree.mirror : BinTree α → BinTree α
| .leaf => .leaf
| .branch l x r => .branch (mirror r) x (mirror l)
표현식의 기대 타입을 사용해 네임스페이스를 명확히 하는 방식은 생성자가 아닌 다른 이름에도 적용할 수 있습니다. BinTree.empty가 BinTree를 만드는 또 다른 방법으로 정의되어 있다면, 이것 역시 점 표기법과 함께 사용할 수 있습니다:
def BinTree.empty : BinTree α := .leaf#check (.empty : BinTree Nat)4.6.3. Or-패턴
match 표현식처럼 여러 패턴을 허용하는 문맥에서는 여러 패턴이 결과 표현식을 공유할 수 있습니다. 요일을 나타내는 데이터 타입 Weekday는 다음과 같습니다:
inductive Weekday where
| monday
| tuesday
| wednesday
| thursday
| friday
| saturday
| sunday
deriving Repr패턴 매칭을 사용하여 어떤 요일이 주말인지 확인할 수 있습니다:
def Weekday.isWeekend (day : Weekday) : Bool :=
match day with
| Weekday.saturday => true
| Weekday.sunday => true
| _ => false이는 생성자 점 표기법을 사용하면 벌써 간단하게 만들 수 있습니다:
def Weekday.isWeekend (day : Weekday) : Bool :=
match day with
| .saturday => true
| .sunday => true
| _ => false
두 주말 패턴의 결과 표현식(true)이 같으므로 이 둘을 하나로 합칠 수 있습니다:
def Weekday.isWeekend (day : Weekday) : Bool :=
match day with
| .saturday | .sunday => true
| _ => false이는 인자에 이름을 붙이지 않는 형태로 더 간단하게 줄일 수 있습니다:
def Weekday.isWeekend : Weekday → Bool
| .saturday | .sunday => true
| _ => false
내부적으로는 결과 표현식이 각 패턴마다 그대로 복제됩니다. 이는 패턴이 변수를 바인딩할 수 있다는 것을 의미합니다. 다음 예제는 두 생성자 모두 같은 타입의 값을 담고 있는 합 타입에서 inl 생성자와 inr 생성자를 제거합니다:
def condense : α ⊕ α → α
| .inl x | .inr x => x결과 표현식이 복제되기 때문에, 패턴이 바인딩하는 변수들이 같은 타입을 가질 필요는 없습니다. 여러 타입에 대해 동작하는 오버로드된 함수를 사용하면, 서로 다른 타입의 변수를 바인딩하는 패턴들에 모두 적용되는 하나의 결과 표현식을 작성할 수 있습니다:
def stringy : Nat ⊕ Weekday → String
| .inl x | .inr x => s!"It is {repr x}"
실제로는 모든 패턴에서 공통으로 나타나는 변수만 결과 표현식에서 참조할 수 있습니다. 결과가 각 패턴에 대해 의미가 있어야 하기 때문입니다. getTheNat에서는 n에만 접근할 수 있으며, x나 y를 사용하려고 하면 오류가 발생합니다.
def getTheNat : (Nat × α) ⊕ (Nat × β) → Nat
| .inl (n, x) | .inr (n, y) => n
비슷한 정의에서 x에 접근하려고 하면 두 번째 패턴에는 사용할 수 있는 x가 없으므로 오류가 발생합니다:
def getTheAlpha : (Nat × α) ⊕ (Nat × α) → α
| .inl (n, x) | .inr (n, y) => x
결과 표현식이 사실상 패턴 매칭의 각 분기에 그대로 복사되어 붙여넣어진다는 사실은 때때로 놀라운 동작으로 이어질 수 있습니다. 예를 들어, 다음 정의들은 허용되는데, 결과 표현식의 inr 버전이 str의 전역 정의를 참조하기 때문입니다:
def str := "Some string"
def getTheString : (Nat × String) ⊕ (Nat × β) → String
| .inl (n, str) | .inr (n, y) => str
이 함수를 두 생성자에 대해 호출해 보면 혼란스러운 동작이 드러납니다. 첫 번째 경우에는 β가 어떤 타입이어야 하는지 Lean에 알려 주기 위해 타입 표기가 필요합니다:
#eval getTheString (.inl (20, "twenty") : (Nat × String) ⊕ (Nat × String))두 번째 경우에는 전역 정의가 사용됩니다:
#eval getTheString (.inr (20, "twenty"))
Weekday.isWeekend에서처럼 or-패턴을 사용하면 일부 정의를 크게 단순화하고 더 명확하게 만들 수 있습니다. 다만 혼란스러운 동작이 나타날 수 있으므로 사용할 때 주의하는 것이 좋습니다. 특히 여러 타입의 변수가 관련되거나 서로 겹치지 않는 변수 집합이 관련된 경우에는 더욱 주의해야 합니다.