7. 편리한 문법

검토 필요
목, 2006-08-17 20:25 — 귤
Haskell은 원래 문법이랄 게 거의 없다. 그러나 읽고 쓰게 하기 쉽도록 '문법 설탕(syntactic sugar)'을 씌워놓아 그나마 문법이 조금 있다. 람다식을 쓰지 않고 함수를 정의한다든지, 경우표현식 대신 패턴맞춤, 가드, if 를 쓰는 게 그런 경우다. 이외에도 몇 가지 알아두면 편리한 문법 설탕들이 있다.
Haskell에는 변수가 없기 때문에 함수에서 함수로 결과를 직접 넘겨줘야 한다. 그래서 표현이 매우 복잡해질 수 있다. 2차 방정식을 푸는 다음 함수를 보자.
solve a b c | b^2 - 4*a*c >= 0 = ((- b + sqrt (b^2 - 4*a*c)) / 2, (- b - sqrt (b^2 - 4*a*c)) / 2) | otherwise = error "negative d"
2차 방정식에서 b^2 - 4*a*c는 판별식이라고 하는 데 이것은 방정식을 풀 수 있는 지 없는 지 판단할 때도 쓰고, 방정식의 해를 구할 때도쓴다. 똑같은 판별식을 여러 번 사용하기 때문에 쓰기도 귀찮고 읽기도 어렵다. 판별식을 구하는 함수를 따로 만들어봤자,
d a b c = b^2 - 4*a*c solve a b c | d a b c >= 0 = ((- b + sqrt (d a b c)) / 2, (- b - sqrt (d a b c)) / 2) | otherwise = error "negative d"
약간 좋아질 뿐 읽고 쓰기 곤란한 건 여전하다. 게다가 2차 방정식을 풀 때 빼고는 쓸 때가 없는 판별식을 구하려고 함수를 따로 만드는 건 아무래도 낭비다. 이렇게 특정한 표현에서만 사용할 함수를 정의할 때는 where라는 표현을 사용하면 훨씬 간결하고 직관적인 표현을 쓸 수 있다.
solve a b c | d >= 0 = (alpha , beta) | otherwise = error "negative d" where d = b^2 - 4*a*c alpha = (- b + sqrt d) / 2 beta = (- b - sqrt d) / 2
where 안에 있는 함수들끼리 서로 사용하는 것도 가능하다. 어떤 표현에라도 뒤에 where를 붙이고 표현 안에서만 사용할 함수를 정의할 수 있다.
x + 1 where x = 2 ⇒ 2 + 1 ⇒ 3
표현 뒤에 오는 where와 달리 표현 앞에 붙는 let ~ in도 있다.
let x = 2 in x + 1
let ~ in은 가드를 제외하고 아무데나, 심지어 다른 let ~ in 안에서도 쓸 수 있지만, where는 한 표현에 하나만 쓸 수 있다. let ~ in은 독립된 표현인 반면, where는 표현의 일부기 때문이다.
let ~ in이나 where를 쓸 때 주의해야하는 것은 윤곽(layout)이다. Haskell은 들여쓴 깊이가 같은 무장이 이어지면 앞의 문장이 끝났다고 보고. 반대로 한 단계 더 깊이 들여쓰면 앞문장에서 이어진다고 본다. 원래는 문장이 끝날 때마다 세미콜론을 찍어야하지만 이 윤곽 문법 때문에 그럴 필요는 없다.
x = 1 + -- 0칸 들여쓰기 2 -- 0칸 들여쓰기 ⇒ x = 1 +; 2; -- 들여쓴 깊이가 같으므로 별개의 문장
x = 1 + -- 0칸 들여쓰기 2 -- 4칸 들여쓰기 ⇒ x = 1 + 2; -- 더 깊이 들여썼으므로 이어지는 문장
let, where, case ~ of 다음에는 여러 개의 문장이 따라오는 데 이 문장들은 첫 문장만큼 들여써야 한다. 예를 들어 아래 두 표현은 모두 에러가 난다.
xy
xy
왜냐하면 let에서 첫 문장은 x = 1로 들여쓰기 깊이는 let이 아니라 x를 기준으로 하기 때문이다. 따라서 임의대로 들여쓴 첫 문장이나, let을 기준으로 들여쓴 두번 째 문장이나 모두 에러다. 요컨대 x와 y가 같은 위치에서 시작해야 한다는 말이다. 헷갈리지 않으려면 꼭 let, where, case ~ of 다음에 이어지는 문장이 여러 개일 때는 첫 문장을 한 줄 내려쓰는 습관을 들이자.
xy
윤곽문법이 없으면 {, }, ; 등 문장의 시작과 끝을 알리는 귀찮은 기호들을 일일이 달아줘야 한다.
let { x = 1; y = 2; } in x + y;
굳이 여러 줄에 쓸 필요가 없는 짧은 문장들이 여러 개일 때는 세미콜론을 붙여주는 것이 오히려 편할 경우도 있다.
let x = 1; y = 2 in x + y