다음을 통해 공유


코드 서식 지정 지침(F#)

이 항목에서는 F#의 코드 들여쓰기와 관련된 지침을 요약하여 설명합니다. F#은 줄 바꿈과 들여쓰기의 영향을 크게 받는 언어입니다. 이는 단순히 가독성을 높이거나 모양새를 보기 좋게 하려는 차원의 문제가 아니라 코드 서식을 올바르게 지정하는 데 관련된 코드 작성 표준화 문제입니다. 코드를 제대로 컴파일하려면 그 서식을 올바르게 지정해야 합니다.

들여쓰기 일반 규칙

들여쓰기가 필요할 때는 탭이 아니라 공백을 사용해야 합니다. 이때 적어도 한 개 이상의 공백이 필요합니다. 조직에 따라서는 들여쓰기에 사용할 공백의 수를 지정하는 코드 작성 표준 지침을 마련해 뒀을 수도 있습니다. 들여쓰기가 필요한 경우 각 수준별로 들여쓰기에 서너 개의 공백을 사용하는 것이 일반적입니다. 도구 메뉴에서 옵션 대화 상자를 열고 해당 옵션을 변경하여 조직의 들여쓰기 표준에 맞게 Visual Studio를 구성할 수 있습니다. 텍스트 편집기 노드에서 **F#**을 확장한 다음 을 클릭합니다. 사용 가능한 옵션에 대한 자세한 내용은 옵션 대화 상자, 텍스트 편집기, 모든 언어, 탭를 참조하십시오.

일반적으로 컴파일러에서 코드를 구문 분석할 때는 현재 중첩 수준을 나타내는 내부 스택이 그대로 유지됩니다. 코드를 들여쓰면 이 내부 스택에 새 중첩 수준이 만들어지거나 푸시됩니다. 구문이 끝나면 수준이 팝됩니다. 들여쓰기는 한 수준의 끝을 알리고 내부 스택을 팝하는 방법 중 하나입니다. 또는 end 키워드나 닫는 중괄호 또는 닫는 괄호 같은 토큰을 사용하여 수준을 팝할 수도 있습니다.

형식 정의, 함수 정의, try...with 구문, 루프 구문 등과 같이 여러 줄로 이루어진 구문의 코드를 들여쓸 때는 해당 구문의 첫 줄을 기준으로 삼아야 합니다. 처음 들여쓴 줄을 기준으로 하여 동일한 구문의 뒤에 포함된 코드에 대한 열 위치가 설정됩니다. 들여쓰기 수준을 컨텍스트라고 합니다. 동일한 컨텍스트의 뒤에 나오는 코드 줄에 대해서는 열 위치에 따라 오프사이드 줄이라고 하는 최소 열이 설정됩니다. 이렇게 설정된 열 위치보다 적게 들여쓴 코드 줄이 발견되면 컴파일러에서는 컨텍스트가 끝난 것으로 간주하고 한 수준 위의 이전 컨텍스트에서 새로 코드를 작성하는 것으로 판단합니다. 여기서 오프사이드라는 용어는 코드 줄을 충분히 들여쓰지 않아 구문의 끝이 트리거되는 상황을 가리킬 목적으로 사용됩니다. 즉, 오프사이드 줄의 왼쪽에 있는 코드에 대해 해당 코드가 오프사이드되었다고 말할 수 있습니다. 코드를 올바르게 들여쓰면 오프사이드 규칙을 활용하여 구문의 끝을 명확하게 구분할 수 있습니다. 들여쓰기를 부적절하게 사용하면 오프사이드 조건에 의해 컴파일러에서 경고가 발생하거나 코드가 잘못 해석될 수 있습니다.

오프사이드 줄은 다음과 같이 결정됩니다.

  • let과 관련된 = 토큰이 있으면 = 기호 뒤에 있는 첫째 토큰의 열에 오프사이드 줄이 설정됩니다.

  • if...then...else 식에서는 then 키워드나 else 키워드 뒤에 있는 첫째 토큰의 열 위치에 오프사이드 줄이 설정됩니다.

  • try...with 식에서는 try 뒤에 있는 첫째 토큰 위치에 오프사이드 줄이 설정됩니다.

  • match 식에서는 with 뒤에 있는 첫째 토큰과 각각의 -> 뒤에 있는 첫째 토큰 위치에 오프사이드 줄이 설정됩니다.

  • 형식 확장명의 with 뒤에 있는 첫째 토큰 위치에 오프사이드 줄이 설정됩니다.

  • 여는 중괄호 또는 여는 괄호나 begin 키워드 뒤의 첫째 토큰 위치에 오프사이드 줄이 설정됩니다.

  • let, if 및 module 키워드의 첫째 문자 위치에 오프사이드 줄이 설정됩니다.

다음 코드 예제에서는 들여쓰기 규칙을 보여 줍니다. 여기서는 인쇄 문에 들여쓰기를 적용하여 문을 적절한 컨텍스트에 연결하고 있습니다. 들여쓰기가 바뀔 때마다 컨텍스트가 팝하고 이전 컨텍스트로 돌아갑니다. 따라서 매번 반복할 때마다 맨 끝에 공백이 인쇄되고 "Done!"은 한 번만 인쇄됩니다. 이 텍스트는 오프사이드 들여쓰기에 의해 루프에 포함되지 않도록 설정되어 있기 때문입니다. 문자열 "Top-level context"를 인쇄하는 것은 함수를 통해 처리되는 작업이 아닙니다. 따라서 이 문자열은 함수를 호출하기 전에 정적 초기화 과정에서 맨 처음 인쇄됩니다.

let printList list1 =
    for elem in list1 do
        if elem > 0 then
            printf "%d" elem
        elif elem = 0 then
            printf "Zero"
        else
            printf "(Negative number)"
        printf " "
    printfn "Done!"
printfn "Top-level context."
printList [-1;0;1;2;3]

출력은 다음과 같습니다.

Top-level context

(Negative number) Zero 1 2 3 Done!

긴 내용을 여러 줄로 나눠 작성하는 경우 이어지는 줄을 바깥쪽 구문보다 더 많이 들여써야 합니다. 예를 들어 다음 코드에서와 같이 함수 인수를 함수 이름의 첫째 문자보다 더 많이 들여써야 합니다.

let myFunction1 a b = a + b
let myFunction2(a, b) = a + b
let someFunction param1 param2 =
    let result = myFunction1 param1
                     param2
    result * 100
let someOtherFunction param1 param2 =
    let result = myFunction2(param1,
                     param2)
    result * 100

이 규칙에는 몇 가지 예외가 있습니다. 다음 단원에서는 그 예외를 설명합니다.

모듈의 들여쓰기

로컬 모듈의 코드는 모듈을 기준으로 하여 들여써야 하지만 최상위 모듈의 코드는 들여쓸 필요가 없습니다. 네임스페이스 요소는 들여쓸 필요가 없습니다.

다음 코드에서는 이 규칙의 예를 보여 줍니다.

// Program1.fs
// A is a top-level module.
module A

let function1 a b = a - b * b
// Program2.fs
// A1 and A2 are local modules.
module A1 =
    let function1 a b = a*a + b*b

module A2 =
    let function2 a b = a*a - b*b

자세한 내용은 모듈(F#)을 참조하십시오.

들여쓰기 기본 규칙의 예외

이전 단원에서 설명한 것과 같이 여러 줄로 이루어진 구문의 코드는 해당 구문에 있는 첫째 줄의 들여쓰기를 기준으로 하여 들여쓰고 오프사이드 줄이 처음 발견되는 지점에서 구문이 끝난다고 해석하는 것이 일반적인 규칙입니다. 그런데 컨텍스트가 끝나는 지점을 판단하는 규칙에는 예외가 있습니다. try...with 식, if...then...else 식, 상호 재귀 함수나 형식을 선언하기 위해 사용하는 and 구문 등과 같은 일부 구문은 여러 부분으로 이루어집니다. 이러한 구문에서 if...then...else 식의 then 및 else 같이 뒤에 오는 부분을 작성할 때는 그 들여쓰기 위치가 식을 시작하는 토큰과 같은 수준에 놓이더라도 컨텍스트의 끝을 가리키는 것이 아니라 동일한 컨텍스트의 다음 부분을 나타냅니다. 따라서 if...then...else 식을 다음 코드 예제에서와 같이 작성할 수 있습니다.

let abs1 x =
    if (x >= 0)
    then
        x
    else
        -x

오프사이드 규칙의 예외는 then 및 else 키워드에만 적용됩니다. 따라서 then 및 else를 더 많이 들여쓴다고 해서 오류가 발생하지는 않지만 then 블록의 코드 줄을 들여쓰지 않으면 경고가 발생합니다. 다음 코드 줄에서는 이 사실을 보여 줍니다.

// The following code does not produce a warning.
let abs2 x =
    if (x >= 0)
        then
        x
        else
        -x
// The following code is not indented properly and produces a warning.
let abs3 x =
    if (x >= 0)
    then
    x
    else
    -x

else 블록의 코드에는 다른 특별한 규칙이 추가로 적용됩니다. 위 예제의 경고는 else 블록의 코드가 아니라 then 블록의 코드에 대해서만 발생합니다. 따라서 함수의 else 블록에 포함될 수 있는 나머지 코드 부분을 들여쓰지 않고도 함수의 시작 부분에서 여러 가지 조건을 검사하는 코드를 작성할 수 있습니다. 이 경우 다음과 같은 코드를 작성해도 경고가 발생하지 않습니다.

let abs4 x =
    if (x >= 0) then x else
    -x

규칙의 또 다른 예외로서 + 및 |> 같은 중위 연산자의 경우 이전 줄만큼 들여쓰지 않은 줄이 있으면 컨텍스트가 끝난 것으로 간주합니다. 중위 연산자로 시작하는 줄은 일반적인 위치보다 (1 + oplength)열 앞에서 시작하더라도 컨텍스트의 끝이 트리거되지 않습니다. 여기서 oplength는 연산자를 구성하는 문자의 수입니다. 따라서 연산자 뒤의 첫째 토큰을 이전 줄과 나란히 맞출 수 있습니다.

예를 들어 다음 코드에서 + 기호는 이전 줄보다 2개 열만큼 적게 들여쓸 수 있습니다.

let function1 arg1 arg2 arg3 arg4 =
    arg1 + arg2
  + arg3 + arg4

중첩 수준이 올라갈수록 들여쓰기가 증가하는 것이 일반적이지만 들여쓰기를 더 낮은 열 위치부터 다시 시작하도록 컴파일러에서 허용하는 구문도 있습니다.

열 위치를 다시 설정할 수 있는 구문은 다음과 같습니다.

  • 익명 함수의 본문. 다음 코드에서 인쇄 식은 fun 키워드보다 왼쪽에 있는 열 위치에서 시작합니다. 그러나 이 줄은 이전 들여쓰기 수준의 시작 위치보다 왼쪽에 있는 열, 즉 List의 L 왼쪽에 있는 열에서 시작할 수 없습니다.

    let printListWithOffset a list1 =
        List.iter (fun elem ->
            printfn "%d" (a + elem)) list1
    
  • 들여쓰기가 if 키워드의 열 위치보다 적지 않은 경우에 한해 if...then...else 식의 then 또는 else 블록에서 괄호 또는 begin과 end로 묶은 구문. 코드 스타일에 이 예외를 활용하면 then 또는 else 뒤에서 줄이 끝나는 지점에 여는 괄호나 begin을 사용할 수 있습니다.

  • begin...end, {...}, class...end 또는 interface...end로 구분되는 모듈, 클래스, 인터페이스 및 구조체의 본문. 스타일에 이 예외를 활용하면 형식 정의를 여는 키워드보다 전체 본문을 더 많이 들여쓰지 않고 해당 키워드를 형식 이름과 같은 줄에 배치할 수 있습니다.

    type IMyInterface = interface
       abstract Function1: int -> int
    end
    

참고 항목

기타 리소스

F# 언어 참조