12장 컴파일러 프래그마
12장 컴파일러 프래그마 (Compiler Pragmas)
Haskell 코드를 짜다 보면, 컴파일러에게 "이 함수는 인라인해서 써 줘", "이 타입은 미리 전문화해 두면 좋겠어" 같은 부탁을 하고 싶을 때가 있어요. 언어 자체로는 그런 말을 할 자리가 없는데, 그래서 실무에선 컴파일러 프래그마(pragma)라는 장치를 씁니다. 이번 장에서는 Haskell 2010 보고서가 정리해 둔 프래그마 표준 관례(established practice)를 살펴볼게요.
출처: https://www.haskell.org/onlinereport/haskell2010/haskellch12.html
본문
어떤 컴파일러 구현들은 **프래그마(pragma)**를 지원해요. 프래그마는 컴파일러에게 추가 지시나 힌트를 주기 위한 것이지, Haskell 언어 그 자체의 일부도 아니고 프로그램의 의미(semantics)도 바꾸지 않아요. 이 장에서는 이런 기존 관행을 요약한답니다. 구현이 어떤 프래그마를 꼭 지킬 필요는 없지만, 구현이 알아보지 못하는 프래그마는 그냥 무시해야 해요. 그리고 실무에서 쓰이는 언어 확장이 많으니, 구현들이 아래에 설명할 LANGUAGE 프래그마는 지원하도록 강력히 권장하고요.
어휘적으로(lexically) 보면 프래그마는 주석처럼 나타나는데, 다만 감싸는 문법이 {-# #-}라는 점이 달라요.
12.1 인라인 (Inlining)
decl → {-# INLINE qvars #-}
decl → {-# NOINLINE qvars #-}
INLINE 프래그마는 컴파일러에게 지정한 변수들을 사용하는 지점(use site)에서 인라인하라고 지시해요. 컴파일러는 보통 간단한 식을 자동으로 인라인하곤 하는데, 그 자동 동작을 막고 싶다면 NOINLINE 프래그마를 쓰면 되죠.
12.2 전문화 (Specialization)
decl → {-# SPECIALIZE spec₁, …, specₖ #-} (k ≥ 1)
spec → vars :: type
전문화(Specialization)는 오버로드된 함수를 호출할 때 생기는 비효율을 피하려고 써요. 예를 들어 이런 코드가 있다고 해 볼게요.
factorial :: Num a => a -> a
factorial 0 = 0
factorial n = n ⋆ factorial (n-1)
{-# SPECIALIZE factorial :: Int -> Int,
factorial :: Integer -> Integer #-}
이렇게 해 두면, 컴파일러가 인자가 Int인지 Integer인지를 알아낼 수 있는 factorial 호출은, 오버로드된 수치 연산을 거치지 않는 **전문화된 버전의 factorial**을 사용하게 돼요.
12.3 언어 확장 (Language extensions)
LANGUAGE 프래그마는 **파일 헤더 프래그마(file-header pragma)**예요. 파일 헤더 프래그마는 소스 파일에서 module 키워드보다 앞에 와야 하고, 개수에 제한 없이 여러 개를 둘 수 있으며, 앞뒤로 주석이 와도 괜찮아요. 개별 언어 프래그마는 LANGUAGE 키워드로 시작하고, 그 뒤에 쉼표로 구분된 이름 붙은 언어 기능들의 목록이 따라와요.
예를 들어, 여러분의 Haskell 구현이 그 확장들을 지원한다면, 스코프 있는 타입 변수와 CPP 전처리를 이렇게 켤 수 있어요.
{-# LANGUAGE ScopedTypeVariables, CPP #-}
만약 어떤 Haskell 구현이 소스 파일이 요청한 특정 언어 기능을 알아보지 못하거나 지원하지 못한다면(혹은 요청한 언어 기능들의 조합을 지원할 수 없다면), 그 파일을 그 구현으로 컴파일하거나 쓰려는 어떤 시도든 반드시 오류로 실패해야 해요.
이식성(portability)을 위해, 같은 지원 가능한 언어 기능을 여러 번 켜려는 시도(가령 커맨드라인 인자, 구현별 기능 의존성, 비표준 프래그마를 통해서라도)는 명시적으로 허용돼요. LANGUAGE 프래그마를 지원하는 Haskell 2010 구현은 반드시 이걸 지원해야 하고:
{-# LANGUAGE Haskell2010 #-}
그 구현들은 또한 다음 이름 붙은 언어 기능들을 지원하도록 권장돼요:
PatternGuards, NoNPlusKPatterns, RelaxedPolyRec,
EmptyDataDecls, ForeignFunctionInterface
이것들은 어떤 pre-Haskell 2010 구현들이 지원하던 이름 붙은 언어 확장들인데, 이번 보고서에 통합된 것들이랍니다.
더 알아보기 (Learn more)
- 프래그마 문법의 어휘 구조(주석과의 차이)는 Haskell 2010 보고서 12장에서 다뤄요.
- Haskell 2010 보고서 전체 구조와 다른 장 목록은 Reports 지수를 참고하세요.
- 실제 컴파일러에서 쓰이는 다양한 프래그마(INLINE, SPECIALIZE, LANGUAGE 등)의 구체적 동작은 GHC 사용자 가이드의 프래그마 섹션에서 더 자세히 볼 수 있어요.