Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

부록 A: 표면 구문

이 문법은 최종 Fun 언어를 한곳에 모아 둔 것입니다. 이 문법은 레이아웃이 중괄호와 세미콜론을 삽입한 뒤의 소스 구문을 기술합니다. 이름 해석, 카인드, 타입 검사는 아래에 나열한 제약을 부과합니다. 이에 대응하는 판단은 부록 B를 참고하십시오.

문법 읽는 법

따옴표로 묶인 문자열은 종단 기호입니다. |는 대안을 구분하고, [ X ]는 생략 가능함을, { X }는 0회 이상의 반복을 뜻합니다. 괄호는 문법 표현식을 묶습니다. 따옴표로 묶인 대괄호와 중괄호는 리터럴 토큰입니다. varid, conid, 리터럴 토큰은 어휘 절에서 정의합니다. 중괄호가 없는 소스 파일은 바깥쪽 레이아웃 블록을 받으므로, 두 스타일 모두 같은 program 생성 규칙을 가집니다.

선언

program     ::= '{' [ topdecl { ';' topdecl } ] [ ';' ] '}'
topdecl     ::= datadecl | typedecl | classdecl | instdecl
              | fixity | sig | bind

datadecl    ::= 'data' tycon { tyvar } '=' constr
                { '|' constr } [ deriving ]
constr      ::= con { atype }
              | con '{' fielddecl { ',' fielddecl } '}'
fielddecl   ::= varid { ',' varid } '::' type
deriving    ::= 'deriving' conid
              | 'deriving' '(' conid { ',' conid } ')'
typedecl    ::= 'type' conid { tyvar } '=' type

classdecl   ::= 'class' [ context '=>' ] conid tyvar
                [ 'where' '{' [ member { ';' member } ] [ ';' ] '}' ]
member      ::= sig | bind
instdecl    ::= 'instance' [ context '=>' ] conid insthead
                [ 'where' '{' [ bind { ';' bind } ] [ ';' ] '}' ]
insthead    ::= conid | '(' conid { tyvar } ')'
              | '[' tyvar ']' | '(' ')'
              | '(' tyvar ',' tyvar [ ',' tyvar ] ')'
fixity      ::= ('infixl' | 'infixr' | 'infix') digit op { ',' op }
sig         ::= var { ',' var } '::' qualtype

bind        ::= funlhs rhs | pat rhs
funlhs      ::= var { apat } | apat varop apat
rhs         ::= '=' expr [ 'where' decls ]
              | guard '=' expr { guard '=' expr } [ 'where' decls ]
guard       ::= '|' expr
decls       ::= '{' [ localdecl { ';' localdecl } ] [ ';' ] '}'
localdecl   ::= sig | bind

레코드 선언은 정확히 하나의 생성자를 가집니다. 레코드의 레이블은 서로 달라야 하지만, 다른 레코드 타입이 같은 레이블을 다시 쓸 수는 있습니다. 인스턴스 헤드의 변수는 서로 달라야 하며, 컨텍스트는 그 변수들만 언급합니다. 결합성 선언은 최상위 수준에서만 나타나며, 우선순위는 0부터 9까지입니다. deriving할 수 있는 클래스는 Eq, Ord, Show뿐입니다. 패턴 바인딩은 재귀적이지 않습니다. 재귀 바인딩은 디슈거링 후에 함수가 되어야 합니다.

표현식

expr        ::= infixexp [ '::' qualtype ]
infixexp    ::= lexp { op lexp } | '-' infixexp
lexp        ::= '\' apat { apat } '->' expr
              | 'let' decls 'in' expr
              | 'if' expr 'then' expr 'else' expr
              | 'case' expr 'of' '{' alt { ';' alt } [ ';' ] '}'
              | 'do' '{' stmt { ';' stmt } [ ';' ] '}'
              | fexp
fexp        ::= aexp { aexp }
aexp        ::= atom { projection | update }
atom        ::= var | con | literal
              | '(' expr ')'
              | '(' expr ',' expr [ ',' expr ] ')'
              | '[' [ expr { ',' expr } ] ']'
              | '[' expr '..' expr ']'
              | '(' infixexp op ')' | '(' op infixexp ')'
              | '(' PROJ_SECTION varid ')'
              | conid '{' fbind { ',' fbind } '}'
projection  ::= PROJ varid
update      ::= '{' fbind { ',' fbind } '}'
fbind       ::= varid '=' expr
alt         ::= pat ( '->' expr | guard '->' expr
                { guard '->' expr } ) [ 'where' decls ]
stmt        ::= expr | pat '<-' expr | 'let' decls

var         ::= varid | '(' varsym ')'
con         ::= conid | '(' ':' ')' | '[' ']' | '(' ')'
              | '(' ',' ')' | '(' ',' ',' ')'
op          ::= varsym | '`' varid '`' | ':'
varop       ::= varsym | '`' varid '`'

블록의 마지막 do 문장은 표현식이어야 합니다. 맨 생성자 뒤에 레코드 중괄호가 오면 갱신이 아니라 생성입니다. 생성에는 모든 필드가 정확히 한 번씩 필요합니다. 갱신은 레코드의 타입을 보존합니다. 튜플의 항수는 2 또는 3입니다. (- e)는 오른쪽 섹션이 아니라 부정입니다. (&&)와 (||)는 값으로 쓸 수 있지만, 이 연산자들은 재정의할 수 없습니다.

프로젝션과 갱신은 적용보다 더 강하게 결합합니다. 따라서 f r.x는 f (r.x)를 뜻합니다. 적용은 왼쪽으로 결합합니다. 화살표 타입은 오른쪽으로 결합합니다. 연산자 연쇄는 스코프 안의 선언을 사용해 해석하며, 기본값은 infixl 9입니다. 단항 마이너스는 Haskell의 우선순위 6 규칙을 따릅니다. 같은 우선순위에서 결합 방향이 충돌하면 괄호가 필요합니다.

Prelude는 다음과 같은 결합성을 제공합니다.

결합성우선순위연산자
오른쪽9.
왼쪽7*, /, `div`, `mod`, `quot`, `rem`
왼쪽6+, -
오른쪽6<>
오른쪽5:, ++
비결합4==, /=, <, <=, >, >=
왼쪽4<$>, <*>
오른쪽3&&
오른쪽2||
왼쪽1>>=, >>
오른쪽0$

패턴과 타입

pat         ::= lpat [ ':' pat ]
lpat        ::= conid apat { apat } | '-' intlit | apat
apat        ::= varid [ '@' apat ] | '_' | con
              | conid '{' [ fpat { ',' fpat } ] '}'
              | literal | '(' pat ')'
              | '(' pat ',' pat [ ',' pat ] ')'
              | '[' [ pat { ',' pat } ] ']'
fpat        ::= varid '=' pat

qualtype    ::= [ context '=>' ] type
context     ::= pred | '(' pred { ',' pred } ')'
pred        ::= conid tyvar
type        ::= btype [ '->' type ]
btype       ::= atype { atype }
atype       ::= tycon | tyvar | '(' type ')'
              | '(' type ',' type [ ',' type ] ')'
              | '[' type ']'
tycon       ::= conid | '[' ']' | '(' ')'
              | '(' ',' ')' | '(' ',' ',' ')' | '(' '->' ')'
tyvar       ::= varid

하나의 패턴 벡터 안에서 패턴 변수는 서로 달라야 합니다. 생략된 레코드 필드는 와일드카드를 뜻합니다. 패턴 안의 생성자는 포화되어 있어야 합니다. 타입 동의어는 완전히 적용되어야 하며 순환해서는 안 됩니다. 명시적 forall, 카인드 표기, 지연 패턴, 한정된 이름, 사용자 정의 중위 생성자는 없습니다.

어휘 구조와 레이아웃

식별자는 ASCII로 이루어집니다. varid는 [a-z_][A-Za-z0-9_']*에, conid는 [A-Z][A-Za-z0-9_']*에 대응합니다. _ 하나만 있으면 와일드카드입니다. 예약어는 다음과 같습니다.

case class data deriving do else if in infix infixl infixr
instance let of then type where

연산자는 아래의 예약 연산자를 제외하고, 다음 기호 문자들이 최대한 길게 이어진 것입니다. 사용자 정의 연산자는 :로 시작할 수 없습니다.

symbol characters: ! # $ % & * + . / < = > ? @ \ ^ | ~ : -
reserved operators: .. : :: = \ | <- -> @ =>

정수 리터럴은 10진수 또는 0x 16진수이며, 밑줄을 구분자로 쓸 수 있습니다. Double 리터럴은 1.5, 1e10, 2.5e-3처럼 소수점이나 지수를 포함합니다. 부호는 리터럴 토큰 바깥에 있는 구문입니다. 문자 리터럴은 작은따옴표 안에 유니코드 스칼라 하나를 담습니다. 문자열은 큰따옴표 안에 유니코드 텍스트를 담습니다. 둘 다 \n, \t, \\, \', \", \0, \u{…} 이스케이프를 지원합니다. literal은 정수, Double, 문자, 문자열 토큰을 아우릅니다.

줄 주석은 -- 뒤에 기호가 아닌 문자가 오면 시작됩니다. -->는 연산자입니다. 블록 주석은 {-와 -}를 사용하며 중첩될 수 있습니다.

레이아웃 처리 전에, 인접 여부로 프로젝션 점을 구별합니다. 변수, 닫는 구분자, 리터럴 바로 뒤에 있으면서 varid 바로 앞에 있는 점은 PROJ입니다. ( 바로 뒤이면서 varid 앞에 있으면 PROJ_SECTION입니다. 그 밖의 경우에는 일반 연산자입니다. 따라서 f.g는 필드를 프로젝션하고, f . g는 함수를 합성합니다.

레이아웃은 let, where, of, do 뒤와 중괄호가 없는 파일 전체 둘레에 중괄호를 삽입합니다. 들여쓰기가 같으면 선언이나 문장이 서로 구분됩니다. 들여쓰기가 줄어들면 블록이 닫힙니다. 명시적인 중괄호가 우선순위를 가지며, in은 같은 줄에 있더라도 암묵적인 let 블록을 닫습니다. 8장에서 삽입된 토큰을 직접 보여 줍니다. Haskell Report는 그 바탕이 되는 어휘 규칙과 레이아웃 규칙을 제시합니다 (Simon Marlow 2010).


원문 © Stephen Diehl · Write You a Haskell · 산문 CC BY-SA 4.0 · 코드 MIT
yeokja 한국어 번역 · Anthropic 사의 claude-opus-5-5 모델을 활용하여 번역되었으며 학습을 모두 비허용한 상태로 작업하였습니다 · 이 문서는 원문을 수정한 비공식 번역본이며 산문은 같은 CC BY-SA 4.0으로 제공합니다.